zoukankan      html  css  js  c++  java
  • poj 3903 & poj 2533 最长上升子序列(LIS)

    最长上升子序列。

    做这道题之前先做了2533,再看这道题,感觉两道题就一模一样,于是用2533的代码直接交, TLE了;

    回头一看,数据范围。2533 N:0~1000;3903 N :1~100000。

    原因终归于算法时间复杂度。

    也借这道题学习了nlgn的最长上升子序列。(学习链接:http://blog.csdn.net/dangwenliang/article/details/5728363)

    下面简单介绍n^2 和 nlgn 的两种算法。

    n^2:

    主要思想:DP;

    假设A1,A2...Ak已经为上升子序列,下一个遇到的数是An,那么下面会遇到两种情况;

    (1) An <= Ak;这种情况,则所得上升子序列仍为A1—Ak

    (2) An > Ak;这种情况,将An添加到Ak后面,则使得新的上升子序列(A1,A2,...,Ak,An)长度为 k+1( > k);

    由此可得状态转移方程为: dp[i] = max( dp[ i ], dp[ j ]  + 1). 其中 i < j < n;

     1 #include<stdio.h>
     2 #include<algorithm>
     3 
     4 using namespace std;
     5 
     6 const int N = 1024;
     7 int seq[N];
     8 int dp[N];
     9 
    10 int main(){
    11 
    12   int n;
    13 
    14   while(~scanf("%d", &n)){
    15 
    16     for(int i = 1; i <= n; ++i){
    17         scanf("%d", &seq[i]);
    18         dp[i] = 1; // 初始长度为1
    19     }
    20     for(int i = 2; i <= n; ++i)
    21         for(int j = 1; j < i; ++j)
    22           if(seq[j] < seq[i])
    23             dp[i] = max(dp[i],dp[j] + 1);
    24     int mlen = -1;
    25     for(int i = 1; i <= n; ++i)
    26       mlen = max( mlen,  dp[i] );
    27     printf("%d
    ", mlen);
    28   }
    29 
    30   return 0;
    31 
    32 }
    View Code

    容易看出该时间复杂度为 n^2;

    nlgn:

    主要思想:二分;

    假设A1,A2...Ak已经为上升子序列,若此时存在:

    (1) Am < Ap < An;(1< m < n < k, p > m)

    (2)length of A1...Am, An...AK == length of A1...Ap, An...Ak;

    显然, A1...Ap, An...Ak 会比 A1...Am, An...AK更优。为什么呢?

    如果此时存在 q , p < q < n, 使得Ap < Aq < An,则有长度为 k+1 ( > k)的上升子序列。

    由此,我们可以得出一种思路:

    用一个数组C[ ],来存放长度为 s 的最长上升子序列中的最小值。(1 <= s < len, len为A中最长上升子序列的长度)

    可知C有如下特点:

    (1) C为不下降序列。(此特点为可以使用二分的条件)

    (2) C的长度即为最长上升子序列的长度。

    那么, 对于Clen, 如果下一个要考虑是否添加进入上升序列的元素为At,有At > Clen,则将At添入C的末尾,可得新的上升子序列,此序列长度为 len + 1;

                             如果有At < Clen,则找到 j ,使得Aj为所有小于At中的最大值,若不存在与 At 相等的值,则把At插入 j+1 这个位置,会新得到一个长度为len + 1的上升子序列。(由于C是有序的,所以查找j的时候可以采用二分法);

    但最后得到的C,不是A中的最长上升子序列。( 该算法的正确性我也无法证明,而且没有搜到该算法的证明 )

     1 #include<stdio.h>
     2 
     3 const int N = 100008;
     4 
     5 int num[N], c[N];
     6 
     7 int bin(int *a, int len, int n){
     8 
     9    int left = 0, right = len, mid = (left + right) >> 1;
    10    while(left <= right){
    11 
    12        if(n > a[mid]) left = mid + 1;
    13        else if( n < a[mid] ) right = mid - 1;
    14        else return mid;
    15        mid = ( left + right ) >> 1;
    16 
    17    }
    18    return left;
    19 
    20 }
    21 
    22 int main(){
    23 
    24 
    25    int n;
    26    while(~scanf("%d", &n)){
    27 
    28     for(int i = 0; i < n + 1; i++) //为什么要到n+1呢?
    29         c[i] = 1000000;
    30 
    31     for(int i = 0; i < n; i++)
    32         scanf("%d", &num[i]);
    33     c[0] = -1;//c[0] = -1??为什么
    34     c[1] = num[0];
    35     int len = 1;
    36     for(int i = 1; i < n; i++){
    37         int k = bin(c, n+1, num[i]);
    38         c[k] = num[i];
    39         if( k > len)
    40             len = k;
    41     }
    42     printf("%d
    ", len);
    43    }
    44 
    45    return 0;
    46 
    47 }
    View Code

    此时时间复杂度为nlgn。

                                                                                                                                                                                    END

    如有任何问题,欢迎指教。

  • 相关阅读:
    如何增加学习感悟
    古代到清朝历史及文化
    至少的问题
    两次相遇同一地点
    行测都是选择题总结经验
    DES加密实现的思想及代码
    AES算法
    线性代数中行列与矩阵的联系和区别
    学习phalcon框架按照官网手册搭建第一个项目注册功能
    phpadmin增加使得项目能连接数据库
  • 原文地址:https://www.cnblogs.com/pekary/p/3873164.html
Copyright © 2011-2022 走看看