DP之最长上升子序列并找序列的值

最长上升子序列:

一个序列有N个数:A[1],A[2],…,A[N],求出最长非降子序列的长度。

我们定义d(i),表示前i个数中以A[i]结尾的最长非降子序列的长度。

如果我们已经求出了d(1)d(i-1)那么d(i)可以用下面的状态转移方程得到:

d(i) = max{1, d(j)+1},其中j<i,A[j]<=A[i]

 如果我们要求的这N个数的序列是:

53486,7
///简单找出最长上升子序列的个数
#include<cstdio>
#include<algorithm>
using namespace std;
int main()
{
    int len[100]={0,5,3,4,8,6,7};
    int dp[100],n,maxs;
    n=6; ///假设n已赋值
    maxs=-1;
    for(int i=1;i<=n;i++)
    {
        dp[i]=1; ///自身成为一个长度为1的子序列
        for(int j=1;j<=i;j++) ///在前i个数里找小于len[i]
        {
            if(len[j]<len[i]){
                dp[i]=max(dp[i],dp[j]+1);
            }
        }
        if(dp[i]>maxs)
            maxs=dp[i];
    }
    printf("%d\n",maxs);
    return 0;
}
这时最长上升子序列有多少个我们已经找出来了,但有没有想过怎么找出是哪几个子序列。恕我水平有限,当有多组子序列都满足同样的最长上升子序列长度时,以下只能找出其一种情况,例如

3  7  4  11  13

此序列很简单看出最长上升子序列长度为4;有两种不同的子序列,such as:5 7 11 13 与3 4 11 13.在接下的代码中,我会讲解下如何找出其中一组序列,为什么我不涉略下怎么找出所有的可能的值呢?答案是,一般题目不是那么神经的让你找出所有的子序列,一般是让你找出一组解就行了,但倘若题目真的让你找出所有的解,听君一句劝,拉上你朋友圈,在各大贴吧,论坛,微博......喷飞他。

假设一组数据: 5, 3 ,4, 8, 6, 7

我们来模拟一下,如图

i                d[i]               存储上一级的位置





///找最长上升子序列
#include<cstdio>
#include<algorithm>
using namespace std;
int main()
{
    int len[100]={0,5,3,4,8,6,7};
    int dp[100],n,maxs,item,flag;
    int book[100];
    n=6; ///假设n已赋值
    maxs=-1;
    for(int i=1;i<=n;i++)
        book[i]=i;
    for(int i=1;i<=n;i++)
    {
        dp[i]=1; ///自身成为一个长度为1的子序列
        for(int j=1;j<=i;j++) ///在前i个数里找小于len[i]
        {
            if(len[j]<len[i]){
                if(dp[i]<=dp[j]+1) {dp[i]=dp[j]+1;
                book[i]=j; } ///存储上一级的最长上升子序列的位置
            }
        }
        if(dp[i]>maxs){
            maxs=dp[i];
            item=i;   ///最长上升子序列的最后一个位置
            }
    }
    printf("%d\n",maxs);
    flag=0;
    while(1){ 
        printf("%d ",len[item]);
        item=book[item];
        if(item==book[item]) 
            flag++;  ///一直找,直到此判断成立。我们是从后往前找的,由上表可知
                    ///当找到最初的位置时,此时的item和book[item]是相等的,
        if(flag>1) break; ///所以这就是为什么要再定义个变量flag的原因了。
    }

    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值