最长递增子序列 (LIS) Longest Increasing Subsequence
问题描述:
有一个长为n的数列a0, a1,..., an-1.请求出这个序列中最长的上升子序列。请求出这个序列中最长的上升子序列。
上升子序列:对于任意i<j都满足ai<aj的子序列.
限制条件
i <= n <= 1000
0 <= ai <= 1000000
两种定义方式 具体看程序注释
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 #include <algorithm> 5 #define INF 0x3f3f3f3f 6 using namespace std; 7 8 int n; 9 int a[128]; 10 //定义 dp[i] 以a[i]作为最末数字的的最长子序列 11 //状态转移方程 dp[i] = 1//自己 12 // = max(dp[i], dp[j]+1) //i > j && a[i] > a[j] 将a[i] 添加在a[j]后面 13 int main() 14 { 15 freopen("in.txt", "r", stdin); 16 scanf("%d", &n); 17 for (int i = 0; i < n; i++) 18 { 19 scanf("%d", &a[i]); 20 } 21 int dp[128]; 22 memset(dp, 0, sizeof(dp)); 23 for (int i = 0; i < n; i++) 24 { 25 dp[i] = 1; 26 for (int j = 0; j < i; j++) 27 { 28 if (a[j] < a[i]) dp[i] = max(dp[i], dp[j]+1); 29 } 30 } 31 cout << dp[n-1] << endl; 32 //复杂度 O(n^2) 33 //---------------------------------------------------------------------------// 34 //定义dp[i] 长度为i+1 的序列 的最小结尾值 因为结尾值最小 在后面更新时 越有优势 35 //状态转移方程 dp[i] = min(dp[i], a[j]) 36 fill(dp, dp+128, INF); 37 for (int j = 0; j < n;j++)//注意是要对每一个数从前往后只检查一次 去看能否替换 dp数列中的某个值 38 { 39 for (int i = 0; i < n; i++) 40 { 41 if (i == 0 || dp[i-1] < a[j]) dp[i] = min(dp[i], a[j]);//因为是要求递增 那么比前一个大的话更新这一位 使这一位为最小值 42 } 43 }//这样实现也是O(n^2)的复杂度 44 //但是在查找a[j]的过程 可以使用二分查找 优化这样复杂度变为n*logN 45 int ans = 0; 46 for (int i = 0; i < n; i++) 47 { 48 if (dp[i] < INF) ans = i+1; 49 } 50 cout << ans << endl; 51 //--------------------------------------------------------------------// 52 fill(dp, dp+128, INF); 53 for (int i = 0; i < n; i++) 54 { 55 *lower_bound(dp, dp+n, a[i]) = a[i];//dp[0] 到 dp[n-1] >= a[i] 的最小指针 也就是按照从左到有的顺序 56 } 57 for (int i = 0; i < n; i++) 58 { 59 if (dp[i] < INF) ans = i+1; 60 } 61 cout << ans << endl; 62 return 0; 63 }