POJ 2127 Greatest Common Increasing Subsequence DP 最长公共上升子序列
题意:求最长公共上升子序列。
思路1:O(n^3) 算法
用dp[i][j]为a串匹配到i,b串匹配到j时的最长公共上升子序列的长度。
状态转移方程如下:
dp[i][j] = max(dp[k][t]+1) (1<= k < i && 1 <= t < j && a[i] == b[j] && a[k] < a[i] && b[t] < b[j]) dp[i][j] = max(dp[k][t]) (1<= k <= i && 1 <= t <= j && a[i] != b[j]) (k == i 与 t == j 不同时取到)
复杂度:O(n^4) ,TLE,需要优化。
优化:
我们以a串为基准,重新定义dp[i][j] :以a[i]为基准匹配串末尾时(a[i]必选),匹配到b[j]时的最长公共上升子序列的长度
状态转移方程:
dp[i][j] = dp[i][j-1] (a[i] != b[j]) dp[i][j] = max{ dp[k][j-1] } (1 <= k < i && a[i] == b[j] && a[k] < a[i])
通过这样的定义我们保证了,对于任何dp[i][j], dp[i][j]里面存放的是已当前a[i]为匹配的串的最后一个元素时的最大长度。
代码:
View Code
#include<stdio.h> #include<string.h> #include<iostream> using namespace std; int a[505], b[505], dp[505][505]; int max(int a, int b) { return a > b ? a : b; } void print(int x, int y) // 递归输出路径 { if(x < 1 || y < 1) return; if(a[x] != b[y]) print(x, y-1); else { for(int i = x-1; i >= 1; i--) if( a[i] < a[x] && dp[i][y] + 1 == dp[x][y]) { print(i, y); break; } printf("%d ", a[x]); } } int n, m; int main() { int i, j, k; while( ~scanf("%d", &n)) { for(i = 1; i <= n; i++) scanf("%d", &a[i]); scanf("%d", &m); for(i = 1; i <= m; i++) scanf("%d", &b[i]); memset(dp, 0, sizeof(dp)); for(i = 1; i <= n; i++) for(j = 1; j <= m; j++) if(a[i] == b[j]) { for(k = 0; k < i; k++) // 注意循环不能倒过来,想一想为什么 if(a[k] < a[i] && dp[i][j] < dp[k][j-1] + 1) dp[i][j] = dp[k][j-1] + 1; if(!dp[i][j]) dp[i][j] = 1; } else dp[i][j] = dp[i][j-1]; int ans = 0, k = 0; for(i = 1; i <= n; i++) if(ans < dp[i][m]) ans = dp[i][m], k = i; printf("%d\n", ans); print(k, m); puts(""); } return 0; } /* 3 2 1 2 3 2 1 5 1 2 3 5 */
思路2:O(n^2)算法


浙公网安备 33010602011771号