• 最长上升(不下降)子序列(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]<<' ';
        }
    }
  • 相关阅读:
    robotium 测试android渠道包,采用批处理+robotium脚本的方式
    android手机中360手机卫士 VS 腾讯手机管家 VS 金山手机助手,谁更霸道?
    问题解决:INSTRUMENTATION_STATUS: Error=Unable to find instrumentation info for
    robotium做划屏操作函数scrollToSide ,坑爹
    eclipse创建测试apk文件的测试工程,报错java.lang.NullPointerException
    jdk 1.7下,apk重签名的bat
    aapt.exe查看apk文件的versionCode
    JMETER 2.10的安装
    2.js基础
    1.js基础
  • 原文地址:https://www.cnblogs.com/dyhaohaoxuexi/p/11354468.html
Copyright © 2020-2023  润新知