Greatest Common Increasing Subsequence
Time Limit: 10000MS | Memory Limit: 65536K | |
Total Submissions: 12418 | Accepted: 3229 | |
Case Time Limit: 2000MS | Special Judge |
Description
You are given two sequences of integer numbers. Write a program to determine their common increasing subsequence of maximal possible length.
Sequence S1 , S2 , . . . , SN of length N is called an increasing subsequence of a sequence A1 , A2 , . . . , AM of length M if there exist 1 <= i1 < i2 < . . . < iN <= M such that Sj = Aij for all 1 <= j <= N , and Sj < Sj+1 for all 1 <= j < N .
Sequence S1 , S2 , . . . , SN of length N is called an increasing subsequence of a sequence A1 , A2 , . . . , AM of length M if there exist 1 <= i1 < i2 < . . . < iN <= M such that Sj = Aij for all 1 <= j <= N , and Sj < Sj+1 for all 1 <= j < N .
Input
Each sequence is described with M --- its length (1 <= M <= 500) and M integer numbers Ai (-231 <= Ai < 231 ) --- the sequence itself.
Output
On the first line of the output file print L --- the length of the greatest common increasing subsequence of both sequences. On the second line print the subsequence itself. If there are several possible answers, output any of them.
Sample Input
5 1 4 2 5 -12 4 -12 1 2 4
Sample Outpu2题意:给出两个序列,求最长公共上升子序列的长度,并输出此子序列。题解&总结:1. 最长公共上升子序列问题(LCIS)+dp路径记录,下面第一份代码为LCIS模板代码和注释理解;
2. 由于LCIS可以重复利用一维dp数组而无需开二维dp数组,自然而然地会让人想到存路径能否也用一维,
其实是不行的,用文字解释就是“在一个以满足上升的序列中,会有后面的替换之前的前驱”。比如:
5
2 3 4 1 2
4
1 2 3 4
这组数据中,对照下面第二份代码来看,当i循环到4的时候,dp[1],dp[2],dp[3],dp[4]的前驱(即dp[j].pre)分别为0,0,2,3,
此时还是正确的,但i为5的时候,dp[2].pre会被更新为1(此时的更新只是局部最优解的更新,但会影响到我们全局最优解的路径),
而1显然并不在正确路径中,因此一维存路径是不正确的,有必要用二维数组显示每次更新的前驱对应的i,j分别为多少,才能在后续还原路径时得出正确答案。
LCIS模板:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /* 2 定义状态dp[i][j]表示a[1]~a[i], b[1]~b[j]中, 以b[j]结尾的最长公共上升子序列 3 流程: 4 1.a[i] != b[j], a[i]不能和b[j]匹配,a[i]对答案无贡献,那么 dp[i][j] = dp[i-1][j]; 5 2.a[i] == b[j], dp[i][j] = max(dp[i-1][1~(j-1)]) + 1; 6 在循环的过程中维护maxx = max(dp[i-1][1~(j-1)]); 7 对if (a[i] > b[j] && dp[i-1][j] > maxx)的解释: 8 由于maxx是在a[i] == b[j]的情况下使用的,且在内层循环中a[i]不变, 9 故a[i] > b[j]中维护的一系列maxx即为dp[i-1][1~(j-1)]且当a[i] == b[j]时能有a[i] > maxx所对应的a[i-1]和b[j]; 10 */ 11 /* 12 #include <iostream> 13 #include <cstring> 14 #define MAXN 1010 15 #define REP(i, n) for (int i = 1; i <= n; i++) 16 using namespace std; 17 int a[MAXN], b[MAXN], dp[MAXN][MAXN], n, maxx, ans; 18 19 int main() { 20 while (cin >> n) { 21 ans = 0; 22 memset(dp, 0, sizeof(dp)); 23 REP(i, n) cin >> a[i]; REP(i, n) cin >> b[i]; 24 REP(i, n) { 25 maxx = 0; 26 REP(j, n) { 27 dp[i][j] = dp[i-1][j]; 28 if (a[i] > b[j] && dp[i-1][j] > maxx) maxx = dp[i-1][j]; 29 if (a[i] == b[j]) dp[i][j] = maxx + 1; 30 } 31 } 32 REP(i, n) ans = max(ans, dp[n][i]); 33 cout << ans << endl; 34 } 35 return 0; 36 } */ 37 38 // 一维打法,dp的滚动使用,如01背包 39 #include <iostream> 40 #include <cstring> 41 #define MAXN 1010 42 #define REP(i, n) for (int i = 1; i <= n; i++) 43 using namespace std; 44 int a[MAXN], b[MAXN], dp[MAXN], n, maxx, ans; 45 46 int main() { 47 while (cin >> n) { 48 ans = 0; 49 memset(dp, 0, sizeof(dp)); 50 REP(i, n) cin >> a[i]; REP(i, n) cin >> b[i]; 51 REP(i, n) { 52 maxx = 0; 53 REP(j, n) { 54 if (a[i] > b[j] && dp[j] > maxx) maxx = dp[j]; 55 if (a[i] == b[j]) dp[j] = maxx + 1; 56 } 57 } 58 REP(i, n) ans = max(ans, dp[i]); 59 cout << ans << endl; 60 } 61 return 0; 62 }
一维存路径WA代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <cstring> 3 #include <stack> 4 #define MAXN 510 5 #define REP(i, n) for (int i = 1; i <= n; i++) 6 using namespace std; 7 int a[MAXN], b[MAXN]; 8 struct DP{ 9 int num, pre; 10 } dp[MAXN]; 11 12 int main() { 13 int n, m; 14 while (cin >> n) { 15 int ans = 0, pos; 16 memset(dp, 0, sizeof(dp)); 17 REP(i, n) cin >> a[i]; 18 cin >> m; REP(i, m) cin >> b[i]; 19 REP(i, n) { 20 int maxx = 0; pos = 0; 21 REP(j, m) { 22 if (a[i] > b[j] && dp[j].num > maxx) maxx = dp[j].num, pos = j; 23 if (a[i] == b[j]) dp[j].num = maxx + 1, dp[j].pre = pos; 24 } 25 } 26 REP(i, m) if (dp[i].num > ans) ans = dp[i].num, pos = i; 27 stack<int> sta; 28 while (pos) { 29 sta.push(pos); 30 pos = dp[pos].pre; 31 } 32 cout << ans << endl; 33 while (!sta.empty()) { 34 cout << b[sta.top()] << ' '; 35 sta.pop(); 36 } 37 cout << endl; 38 } 39 return 0; 40 }
AC代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <cstring> 3 #include <stack> 4 #define MAXN 1010 5 #define REP(i, n) for (int i = 1; i <= n; i++) 6 using namespace std; 7 int a[MAXN], b[MAXN]; 8 int dp[MAXN], pre[MAXN][MAXN]; 9 10 int main() { 11 int n, m; 12 while (cin >> n) { 13 REP(i, n) cin >> a[i]; 14 cin >> m; 15 REP(i, m) cin >> b[i]; 16 memset(pre, -1, sizeof(pre)); 17 memset(dp, 0, sizeof(dp)); 18 REP(i, n) { 19 int maxx = 0, posj = 0; 20 REP(j, m) { 21 if (a[i] > b[j] && dp[j] > maxx) maxx = dp[j], posj = j; 22 if (a[i] == b[j]) dp[j] = maxx + 1, pre[i][j] = posj; 23 } 24 } 25 int k = 1; 26 REP(i, m) if (dp[i] > dp[k]) k = i; 27 cout << dp[k] << endl; 28 if (!dp[k]) continue; 29 stack<int> ans; 30 for (int i = n; i; i--) if (pre[i][k] != -1) ans.push(a[i]), k = pre[i][k]; 31 cout << ans.top(); ans.pop(); 32 while (!ans.empty()) cout << ' ' << ans.top(), ans.pop(); 33 cout << endl; 34 } 35 return 0; 36 }