动态规划——最长不下降子序列(lis)

题目:http://www.sqyoj.club/problem.php?id=1044

分析:

以样例为例说明。

13 7 9 16 38 24 37 18 44 19 21 22 63 15

用a[1..14]记录关键字,

用b[i]记录以a[1]至a[i]的最长不下降子序列的长度,并初始化b[i]=1。

阶段、状态、决策(以顺推法说明):

第1阶段:

a[1]=13  a[2]=7。初始状态为b[1]=1,目标状态是计算b[2]。

因为a[1..1]中比a[2]小的没有,所以b[2]=1。

第2阶段:

a[1]=13  a[2]=7,a[3]=9。初始状态为b[1]=1,b[2]=1,目标状态是计算b[3]。

因为a[1..2]中比a[3]小的是a[2],所以b[3]=b[3]+b[2],从而b[3]=2。

第3阶段:

a[1]=13  a[2]=7,a[3]=9,a[4]=16。初始状态为b[1]=1,b[2]=1,b[3]=2,目标状态是计算b[4]。

因为a[1..3]中比a[4]小且b[]最大的是a[3],所以b[4]=b[4]+b[3],从而b[4]=3。

从而总结出:

第k阶段:

以b[1..k]为初始状态,目标状态是b[k+1]。

状态转移方程是

b[k+1]=b[k+1]+max{b[j] | a[j] < a[k+1] ,  1<=j<=k }

满足最优子结构:计算出a[1]到a[k]的最长不降子序列后,肯定已经计算出了a[1]到a[i]的最长不降子序列(i=1,2,...,k-1)。

满足无后效性:当计算a[1]到a[k+1]的最长不降子序列长度,是以b[1..k]为初始状态,至于这些初始状态是怎样计算出来的,并不用关心,这就是未来与过去无关。

AC代码如下(逆推法):

#include<cstdio>
#include<iostream>
using namespace std;
int n,a[101][3];
int main(){
    int n=1;
    while(scanf("%d",&a[n][0])!=EOF){
        a[n][1]=1;
        a[n][2]=0;
        n++;
    }
    n--;
    for(int i=n-1;i>=1;i--){//阶段
        int k=i,maxln=0;
        for(int j=i+1;j<=n;j++)//状态
            if(a[j][0]>a[i][0] && a[j][1]>maxln) {//决策
                maxln=a[j][1];
                k=j;
            }
        if(k!=i){
            a[i][1]+=a[k][1];
            a[i][2]=k;
        }
    }
    int maxk=1;
    for(int i=1;i<=n;i++)
       if(a[i][1]>a[maxk][1])maxk=i;
    cout<<"max="<<a[maxk][1]<<endl;
    cout<<a[maxk][0];
    int next=a[maxk][2];
    while(next){
        cout<<' '<<a[next][0];
        next=a[next][2];
    }
    return 0;
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值