对于两个数列A和B,如果它们都包含一段位置不一定连续的数,且数值是严格递增的,那么称这一段数是两个数列的公共上升子序列,而所有的公共上升子序列中最长的就是最长公共上升子序列了。
前驱问题:最长上升子序列、最长公共子序列
状态标表示:f[i][j] 表示A串中前i个字符,以B串中的第j个字符结尾的最长公共子序列的长度
状态转移:
(1)如果A[i] == B[j], 那么从B中的前j个字符中遍历,b[k] < b[j] && k <= j , f[i][j] = max(f[i][j], f[i][k] + 1);
(2)如果A[i] != B[j], 那么相当于从A的前 i - 1 个字符中,以B[j]结尾的公共子序列长度的最大值f[i][j] = f[i - 1][j];
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 3010;
int a[N],b[N];
int f[N][N];
int n;
int main(void)
{
scanf("%d",&n);
for(int i = 1; i <= n; i ++) scanf("%d",&a[i]);
for(int i = 1; i <= n; i ++) scanf("%d",&b[i]);
for(int i = 1; i <= n; i ++)
{
for(int j = 1; j <= n; j ++)
{
f[i][j] = f[i - 1][j];//选择不用a[i];
if(a[i] == b[j])
{
f[i][j] = max(f[i][j], 1);
for(int k = 1; k <= j; k ++)
{
if(b[k] < b[j])
f[i][j] = max(f[i][j], f[i][k] + 1);
}
}
}
}
int res = 0;
for(int i = 1; i <= n; i ++) res = max(res, f[n][i]);
printf("%d",res);
return 0;
}
观察第一层循环和第三层循环,发现第三层循坏在第一次循环时已经处理过 这里 a[i] == b[j],
即第三层中if为 if(b[k] < a[i]) 这里k的范围是1~j,因此只需要处理出当a[i] > b[j] 时f[i][k] + 1的最大值,这在第二层循环的时候可以维护
优化代码
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 3010;
int a[N],b[N],n;
int f[N][N];
int main(void)
{
scanf("%d",&n);
for(int i = 1; i <= n; i ++) scanf("%d",&a[i]);
for(int j = 1; j <= n; j ++) scanf("%d",&b[j]);
for(int i = 1; i <= n; i ++)
{
int m = 1;
for(int j = 1; j <= n; j ++)
{
f[i][j] = f[i - 1][j];
if(a[i] > b[j]) m = max(m, f[i][j] + 1);
if(a[i] == b[j]) f[i][j] = max(f[i][j], m);
}
}
int res = 0;
for(int i = 1; i <= n; i ++) res = max(res, f[n][i]);
printf("%d",res);
return 0;
}