传送门
题意:给你两串数列,让你求它俩的最长公共上升子序列
思路:令f[i][j]表示以a[1-i], b[1-j]构成的以b[j]为结尾的最长上升子序列,不难推出状态转移方程
if(a[i] != b[j]) f[i][j] = f[i-1][j]
if(a[i] == b[j]) f[i][j] = max(dp[i][j], dp[i-1][k])
, 其中k满足0<k<j,a[i]>b[k]中最大的dp[i-1][k]。
状态方程是写出来了,但实现上还有一些细节需要注意。先附上代码
#include<iostream>
#include<cstdio>
using namespace std;
int dp[3001][3001]; //dp[i][j]代表a[1~i]和b[1~j]的LCIS
int a[3001], b[3001];
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
int n;
cin >> n;
for(int i = 1; i <= n; ++i)
cin >> a[i];
for(int j = 1; j <= n; ++j)
cin >> b[j];
int maxx = 0;
for(int i = 1; i <= n; ++i){
for(int j = 1; j <= n; ++j){
if(a[i] != b[j]){
dp[i][j] = dp[i-1][j]; //注意是不能是dp[i][j-1]
}
else{
for(int k = 0; k < j; ++k){ //注意k要从0开始
if(a[i] > b[k])
dp[i][j] = max(dp[i][j], dp[i-1][k]+1);
}
}
if(maxx < dp[i][j])
maxx = dp[i][j];
}
}
cout << maxx << endl;
return 0;
}
首先,为什么是f[i][j] = f[i-1][j]而不是f[i][j] = f[i][j-1],因为根据状态的定义,必须要以b[j]结尾才行。而且可以看出,对于相同的i,k的集合是在不断扩大的,因此我们只需在j的循环里面不断更新k即可。可以变成二维复杂度
#include<iostream>
#include<cstdio>
using namespace std;
int dp[3001][3001]; //dp[i][j]代表a[1~i]和b[1~j]的LCIS
int a[3001], b[3001];
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
int n;
cin >> n;
for(int i = 1; i <= n; ++i)
cin >> a[i];
for(int j = 1; j <= n; ++j)
cin >> b[j];
int maxx = 0;
for(int i = 1; i <= n; ++i){
int val = 0;
for(int j = 1; j <= n; ++j){
if(a[i] != b[j]){
dp[i][j] = dp[i-1][j]; //注意不可以是dp[i][j-1]
}
else{
dp[i][j] = max(dp[i][j], val+1);
}
if(b[j] < a[i])
val = max(val, dp[i-1][j]);
//cout << i << " " << j << " " << dp[i][j] << endl;
if(maxx < dp[i][j])
maxx = dp[i][j];
}
}
cout << maxx << endl;
return 0;
}