• 51nod 最长单增子序列(动态规划)


    最长单增子序列

    (LIS Longest Increasing Subsequence)给定一个数列,从中删掉任意若干项剩余的序列叫做它的一个子序列,求它的最长的子序列,满足子序列中的元素是单调递增的。

    输入

    第1行:1个数N,N为序列的长度(2 <= N <= 50000)
    第2 - N + 1行:每行1个数,对应序列的元素(-10^9 <= S[i] <= 10^9)
    输出
     
    输出最长递增子序列的长度。
     
    输入示例

    8
    5
    1
    6
    8
    2
    4
    5
    10

    输出示例

    5
     
    请选取你熟悉的语言,并在下面的代码框中完成你的程序,注意数据范围,最终结果会造成Int32溢出,这样会输出错误的答案。
    不同语言如何处理输入输出,请查看下面的语言说明。
    【分析】
    我们现在简单讲一下一个O(nlogn)的算法。我们假象一下dp[i][j]表示前i项时构成长度为j的单调子序列的话,最后一项最小的时候是多少。

    如果没有长度为j的单调子序列,则设置为+∞。

    我们证明dp[i], 随着j的增长单调递增(不考虑无穷大的时候)

    初值dp[0][0] = -∞表示长度为0的单调子序列可以达到无穷小。显然dp[0]只有一项值,它是单调递增的。假设dp[i – 1]是单调递增的:

    即 dp[i – 1][0] < dp[i – 1][1] < dp[i – 1][2] < dp[i – 1][3] <..<dp[i – 1][x] 
    其实我们可以加一项dp[i – 1][x + 1] = +∞

    所以 :

    dp[i – 1][0] < dp[i – 1][1] < dp[i – 1][2] < dp[i – 1][3] <..<dp[i – 1][x]  < dp[i – 1][x + 1] 

    我 们考虑a[i]这一项有什么用。我们需要找到dp[i – 1][y] < a[i]把它接到长度为y的子序列后面,形成一个长度为y + 1的子序列。如果dp[i – 1][y + 1] < a[i], 这说明不属于a[i]这一项,我们考虑前个数也可以形成长度为y + 1的单增子序列,且最后一项更小,所以我们不应该更新它。事实上我们需要找到dp[i – 1][y] < a[i]  && dp[i – 1][y + 1] >= a[i], 这样把a[i]接在长度为y的子序列后面形成一个长度为(y + 1)的子序列,同时结尾更小。
    于是我们有递推关系:

    dp[i][0..y] = dp[i – 1][0..y]
    dp[i][y + 1] = a[i]
    dp[i][y + 2..] = dp[i – 1][y + 2…]

    实际上我就更新了一个值,而更新的这个值的递推式,也同时证明了这个序列的单调性。

    y的存在性,由于我们添加了 -∞和+∞,我们一定能找到满足上述条件的y值。而且根据单调性,我们可以利用二分查找的方法找到这个临界的y值。注意最后找到的y有可能就等于x,然后我们更新的时候,会更新dp[x + 1] = a[i],这样子序列的长度增长了1。

    因为每次只更新一个值,我们dp数组只存第二维就可以了。最终的结果,其实是max {x| dp[x] < +∞}的x。
    时间复杂度,二分是O(logn),所以总时间复杂度是O(nlogn)。
    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    #include <time.h>
    #include <string>
    #include <stack>
    #include <vector>
    #include <set>
    #include <queue>
    using namespace std;
    int n,a[50005],d[50005],i,j,len;
    int binsearch(int x)
    {
        int l = 1, r = len, mid;
        while (l <= r)
        {
            mid = (l + r) >> 1;
            if (d[mid-1] <= x && x < d[mid]) return mid;
            else if (x > d[mid]) l = mid + 1;
            else r = mid - 1;
        }
    }
    int main()
    {
        scanf ("%d", &n);
        for (i = 1; i<= n; i++)
            scanf ("%d", &a[i]);
        memset (d, 0, sizeof (d));
        d[1] = a[1];
        len = 1;
        for (i = 2; i <= n; i++)
        {
            if (a[i] < d[1]) j = 1;
            else if (a[i] > d[len]) j = ++len;
            else j = binsearch (a[i]);
            d[j] = a[i];
        }
        printf ("%d
    ", len);
        return 0;
    }
    View Code

    下面的代码只能过四分之一的数据,因为超时。

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    #include <time.h>
    #include <string>
    #include <stack>
    #include <vector>
    #include <set>
    #include <queue>
    using namespace std;
    int main()
    {
        int n,len,i,j,dp[50001];
        int a[50005];
        cin>>n;
        for(i=0; i<n; i++)cin>>a[i];
        int maxn=1;dp[0]=1;
        for(int i=1; i<n; i++)
        {
            dp[i]=1;
            for(int j=0; j<i; j++)
            {
                if(a[i]>a[j])
                {
                    dp[i]=max(dp[i],dp[j]+1);
                }
            }
            maxn=max(maxn,dp[i]);
        }
        cout<<maxn<<endl;
        return 0;
    }
    View Code
  • 相关阅读:
    win10上使用linux命令
    leetcode--js--Median of Two Sorted Arrays
    leetcode--js--Longest Substring Without Repeating Characters
    Linux常用的命令
    微信小程序
    leetcode—js—Add Two Numbers
    PHPExcel使用
    console控制台的用法
    git中常混淆的操作
    mysql解析json下的某个字段
  • 原文地址:https://www.cnblogs.com/jianrenfang/p/5709149.html
Copyright © 2020-2023  润新知