zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    jchdl
    jchdl
    UVa 10256 (判断两个凸包相离) The Great Divide
    UVa 11168 (凸包+点到直线距离) Airport
    LA 2572 (求可见圆盘的数量) Kanazawa
    UVa 10652 (简单凸包) Board Wrapping
    UVa 12304 (6个二维几何问题合集) 2D Geometry 110 in 1!
    UVa 10674 (求两圆公切线) Tangents
    UVa 11796 Dog Distance
    LA 3263 (平面图的欧拉定理) That Nice Euler Circuit
  • 原文地址:https://www.cnblogs.com/jianrenfang/p/5709149.html
Copyright © 2011-2022 走看看