题目思路
$$
f(i,j):所有由A串前i个元素和B串前j个元素且以b_j结尾元素组成的公共上升子序列的最大长度
$$
$$
集合按照是否包含a_i元素分为:
$$
$$
f(i-1,j):一定不包含a_i元素的公共上升子序列\tag1
$$
$$
一定包含a_i元素,因为是公共子序列所以一定需要满足a_i=b_j
$$
$$
max(f(i-1,k)+1)(1<=k<j\& b_k<a_i)\tag2
$$
$$
找到所有由A串前i-1个元素和B串前k个元素且以b_k结尾元素组成的公共上升子序列的最大长度,加上a_i,b_j这一对的长度
$$
$$
f(i,j)=max((1),(2))
$$
朴素写法:
public static void main(String[] args) {
int n = in.nextInt();
int[] a = new int[n + 1];
int[] b = new int[n + 1];
int[][] dp = new int[n + 1][n + 1];
for (int i = 1; i < n + 1; i++) a[i] = in.nextInt();
for (int i = 1; i < n + 1; i++) b[i] = in.nextInt();
int res = 0;
for (int i = 1; i < n + 1; i++) {
for (int j = 1; j < n + 1; j++) {
dp[i][j] = dp[i - 1][j];
if (a[i] == b[j]) {
dp[i][j] = Math.max(dp[i][j], 1);
for (int k = 1; k < j; k++) {
if (b[k] < b[j])
dp[i][j] = Math.max(dp[i][j], dp[i - 1][k] + 1);
}
}
res = Math.max(res, dp[i][j]);
}
}
out.println(res);
out.flush();
out.close();
}
优化写法:
$$
因为a_i=b_j,将其替换发现
$$
$$
每次循环求得的max是满足a_i > b_k的f(i - 1,k)的前缀最大值
$$
$$
因此可以直接将max提到第一层循环外面,减少重复计算,此时只剩下两重循环。
$$
private static int functino2() {
int n = in.nextInt();
int[] a = new int[n + 1];
int[] b = new int[n + 1];
int[][] dp = new int[n + 1][n + 1];
for (int i = 1; i < n + 1; i++) a[i] = in.nextInt();
for (int i = 1; i < n + 1; i++) b[i] = in.nextInt();
int res = 0;
int max;
for (int i = 1; i < n + 1; i++) {
max = 0;
for (int j = 1; j < n + 1; j++) {
dp[i][j] = dp[i - 1][j];
if (a[i] == b[j]) dp[i][j] = Math.max(dp[i][j], max + 1);
if (a[i] > b[j]) max = Math.max(max, dp[i - 1][j]);
res = Math.max(res, dp[i][j]);
}
}
return res;
}