Loading

最长上升(不下降)子序列(LIS) 不同求解方法(动规、贪心)

给定一个序列,求出它的最长上升子序列或者是最长不下降子序列的长度 或者输出这个子序列

一、动态规划 O(n^2)

1.求长度

首先来讨论最长上升子序列的情况,即子序列是严格上升的

假如我们以dp[i]表示以a[i]为结尾的上升子序列的长度 那么对于 j (1<=j<i),如果a[j]<a[i],很显然:

dp[i] = max(dp[i], dp[j]+1);
当然先要赋初值,dp[i]=1,因为一开始只有它自己组成这个序列
最后每次计算出dp[i]之后都要对答案进行更新
至于最长不下降子序列,只需要把a[j]<a[i]改为a[j]<=a[i]即可

代码:

#include <bits/stdc++.h>  

using namespace std; 

int anss=0,i,n,j,a[10005],dp[10005];

int main()
{
    cin>>n;
    for(i=0;i<n;i++)
    {
        cin>>a[i];
    }
    for(i=0;i<n;i++)
    {
        dp[i]=1;
        for(j=0;j<i;j++)
        {
            if(a[j]<a[i])   //如果是最长不下降子序列就改为     if(a[j]<=a[i])
            {
                dp[i]=max(dp[j]+1,dp[i]);
            }
        }
        if(dp[i]>anss)
        anss=dp[i];
    }
    cout<<anss<<endl;
}

2.求序列

只需要从dp数组向前遍历,找到dp[i]==anss的之后再找dp[i]==anss-1的....以此类推即可

#include <bits/stdc++.h>  

using namespace std; 

int anss=0,i,n,j,a[10005],dp[10005];

int main()
{
    cin>>n;
    for(i=0;i<n;i++)
    {
        cin>>a[i];
    }
    for(i=0;i<n;i++)
    {
        dp[i]=1;
        for(j=0;j<i;j++)
        {
            if(a[j]<a[i])
            {
                dp[i]=max(dp[j]+1,dp[i]);
            }
        }
        if(dp[i]>anss)
        anss=dp[i];
    }
    cout<<anss<<endl;
}

不过通常最长上升子序列的解是不唯一的,长度是唯一的。

二、贪心 O(nlogn)

对于一个上升子序列,显然其结尾元素越小,越有利于在后面接其他的元素,也就越可能变得更长。

因此,我们只需要维护 dp 数组(虽然已经不是动态规划了),对于每一个a [ i ],如果a [ i ]能接到 LIS 后面,就接上去;否则,就用 a [ i ] 取更新 dp数组:在dp数组中找到第一个大于等于a [ i ]的元素dp[ j ],用a [ i ]去更新dp [ j ]。怎么找到第一个大于等于的元素呢?只需要使用lower_bound()函数即可。
至于最长不下降子序列,只需要将 “在dp数组中找到第一个大于等于a [ i ]的元素dp[ j ]” 这一步改为找到第一个大于的元素即可。同样的将lower_bound换成upper_bound()

1.代码

#include <bits/stdc++.h>  

using namespace std; 

int anss=0,i,n,j,a[10005],dp[10005],temp;

int main()
{
    cin>>n;
    for(i=0;i<n;i++)
    {
        cin>>a[i];
        dp[i]=0x7ffffff;
    }
    for(i=0;i<n;i++)
    {
        temp=lower_bound(dp,dp+n,a[i])-dp;
        if(temp+1>anss)
        {
            anss++;
        }
        dp[temp]=a[i];
    }
    cout<<anss<<endl;
}

2.求序列

只需要另外开一个数组b,记录dp[i]的位置,然后从b数组向前遍历,找到b[i]==anss的之后再找b[i]==anss-1的....以此类推

#include <bits/stdc++.h>  

using namespace std; 

int anss=0,i,n,j,a[10005],dp[10005],temp,b[10005],c[10005];

int main()
{
    cin>>n;
    for(i=0;i<n;i++)
    {
        cin>>a[i];
        dp[i]=0x7ffffff;
    }
    for(i=0;i<n;i++)
    {
        temp=lower_bound(dp,dp+n,a[i])-dp;
        if(temp+1>anss)
        {
            anss++;
        }
        dp[temp]=a[i];
        b[i]=temp;
    }
    cout<<anss<<endl;
    temp=anss-1;
    for(i=n-1;i>=0;i--)
    {
        if(b[i]==temp)
        {
            c[temp]=a[i];
            temp--;
        }
    }
    for(i=0;i<anss;i++)
    {
        cout<<c[i]<<' ';
    }
}

 

 

posted @ 2019-08-14 20:23  WinterFa1L  阅读(450)  评论(0编辑  收藏  举报