• 动态规划法求最长上升子序列(LIS)


    最长上升子序列(LIS)是指一个序列中最长的单调递增的子序列,对于任意的i<j都满足ai<aj的子序列。

    下面我们来介绍两种dp来求LIS。

    方法1:

    我们首先来建立一下递推关系:

    定义dp[i]:为以ai为末尾的最长上升子序列的长度。

    ai 结尾的上升子序列是:

    (1)只包含 ai 的子序列

    (2)在满足 j<i 并且 aj<ai 的以 aj 为结尾的上升子序列末尾,追加 ai 后得到的子序列

    这二者之一。这样就可以建立如下递推关系:

    dp[i]=max{1,dp[j]+1 j<i且 aj<ai };

    代码如下:

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=1000005;
    int dp[maxn];
    int a[maxn];
    int main()
    {
        int n;
        int res = -1;
        cin >> n;
        for (int i = 1; i <= n; i++)
            cin >> a[i];
        for (int i = 1; i <= n; i++)
        {
            dp[i] = 1;
            for (int j = 1; j < i; j++)
            {
                if (a[j] < a[i])
                    dp[i] = max(dp[i], dp[j] + 1);
            }
                    res=max(res,dp[i]);
        }
        cout << res;
        return 0;
    }    

    这个算法的复杂度为O(n2),当数据量过多时会超时,下面介绍复杂度为O(nlogn)的算法。

    方法2:

     首先我们来定义dp[k]:长度为k的上升子序列的最末元素,若有多个长度为k的上升子序列,则记录最小的那个最末元素。
    注意dp中元素是单调递增的,我们在下面要用到这个性质

    len为LIS的长度,首先让len=1,dp[1]=a[1], 然后i从2开始,对于a[i],如果

    (1) a[i]>dp[len],那么此时可以直接把a[i]接到d[len]的后面,并且长度加一。即d[++len]=a[i]。

    (2) a[i]<dp[len],那么我们就在dp这个数组中找到第一个比a[i]大

    的数,并用a[i]替换这个数,此时数组dp仍然保持递增且长度不变。

    这样我们就维护了数组dp,并且最后的len就是最长上升子序列的长度。算法复杂度为O(nlogn)。

    代码如下:

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=1000005;
    int dp[maxn];
    int a[maxn];
    int main()
    {
        int n;
        cin >> n;
        for (int i = 1; i <= n; i++)
            cin >> a[i];
        int len=1;
        dp[1]=a[1];
        for (int i = 2; i <= n; i++)
        {
               if (a[i] > dp[len])
                 dp[++len]=a[i];
               else{
                int j=lower_bound(dp+1,dp+len+1,a[i])-dp;
                dp[j]=a[i]; 
             }
                    
        }
        cout<<len;
        return 0;
    }    

    下面再补充一个求最长不上升子序列的方法,复杂度同样为O(nlogn)。

    代码如下:

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn = 1000005;
    int a[maxn], dp[maxn];
    bool cmp(int a, int b)
    {
        return a > b;
    }
    int main()
    {
        int n;
        cin >> n;
        for (int i = 1; i <= n; i++)
            cin >> a[i];
        int len = 1;
        dp[1] = a[1];
        for (int i = 2; i <= n; i++)
        {
            if (dp[len] >= a[i])
                dp[++len] = a[i];
            else 
                dp[upper_bound(dp + 1, dp + len + 1, a[i], cmp) - dp] = a[i];
        }
        cout << len;
        return 0;
    }

    这里由于dp数组里的数递减的,而upper_bound适用于递增数列,因此我们给他重载了比较函数。

    这样当 a[i]>dp[len] 时,我们在dp数组中找到最后一个大于a[i]的数(因为非上升子序列,可能会存在相同的数,用upper比较好一点),并替换它。

    如果dp里没有比a[i]大的数,就会返回第一个数,这时a[i]替换的就是dp的第一个数,同样满足dp是递减的数组。

    这样我们就得到了最长不上升子序列的长度len。

  • 相关阅读:
    ubuntu服务器全部署
    果蝇优化算法(FOA)
    小波神经网络(WNN)
    R语言预测实战(第二章--预测方法论)
    ARIMA模型--粒子群优化算法(PSO)和遗传算法(GA)
    R语言预测实战(第一章)
    卷积神经网络(CNN)
    RSS(简易信息聚合)和jieba(第三方分词组件)
    第四章--基于概率论的分类方法:朴素贝叶斯--新闻分类(三)
    第四章--基于概率论的分类方法:朴素贝叶斯--过滤垃圾邮件(二)
  • 原文地址:https://www.cnblogs.com/xiaoguapi/p/10024008.html
Copyright © 2020-2023  润新知