最长上升子序列。
做这道题之前先做了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;
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
容易看出该时间复杂度为 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中的最长上升子序列。( 该算法的正确性我也无法证明,而且没有搜到该算法的证明 )
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
此时时间复杂度为nlgn。
END
如有任何问题,欢迎指教。