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\n", 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\n", len);
43    }
44 
45    return 0;
46 
47 }
View Code

此时时间复杂度为nlgn。

 

                                                                                                                                                                                END

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

posted @ 2014-07-28 15:02  pekary  阅读(200)  评论(0编辑  收藏  举报