求最长公共上升子序列
例题:UVA12511
算法:
dp[i][j]表示a[1] ~ a[i]和b[1] ~ b[j]并以b[j]结尾的最长公共上升子序列,如果a[i]不等于b[j]时,很明显dp[i][j]的值就等于dp[i-1][j];如果a[i]等于b[j]时,就在b[1]~b[j]中寻找b[k]使得b[j]>b[k]而且dp[i][k]是最大的。即状态转移方程为:dp[i][j] = dp[i-1][j](a[i] != b[j]),dp[i][j] = max(dp[i][k])+1(1<=k<j && b[j]>b[k]).
当a[i] == b[j]时,才去遍历寻找max1,是在b[j]>b[k]的条件下,即在a[i]>b[k]所以可以先在[1,m]里面保存好max1的值,然后当a[i] == b[j]时,就可以直接令dp[i][j]=max1+1,时间复杂度就降为O(n^2);
#include<bits/stdc++.h>
using namespace std;
int a[1111],b[1111]; //存A,B串
int dp[1111][1111]; //dp[i][j]表示a[1]~a[i]和b[1]~b[j]并以b[j]结尾的LCIS
int main(){ //本例DP的理解关键是必须以BJ结尾
int t,n,m;scanf("%d",&t); //输出样例数
while(t--){ //样例自减
scanf("%d",&n); //A串长为N
for(int i=1;i<=n; i++){ //遍历
scanf("%d",&a[i]); //读入N个数
dp[i][0] = 0; //因为下标是从1开始的,所以以B[0]结束的LCIS肯定是0
}
scanf("%d",&m); //B串长是M
for(int i=1;i<=m;i++){ //遍历
scanf("%d",&b[i]); //读出M个数
dp[0][i]=0; //因为下标是从1开始的,所以与A[0]匹配的LCIS肯定是0
}
dp[0][0]=0; //定义DP00就是0,没啥好说的,至此初始化及输入完成
int max1; //然后生成DP矩阵
for(int i=1;i<=n;i++){ //大循环A串下标遍历
max1=0;//用于a[i]==b[j]时.寻找前面比b[j](即a[i])小且最大的dp[i][k](1=<k<j)
for(int j=1;j<=m;j++){//二循环B串下标遍历
if(a[i]== b[j])dp[i][j]=max1+1;//匹配成功,当前DP就是最大值加1,这个MAX1保证了前已排好的升序且是最大长度
else dp[i][j]=dp[i-1][j];//不匹配,则当前DP就是与A串的[0~i-1]的LCIS,因为A串的I位对当前DP无贡献,B串还是以BJ结尾,这个是不变的
if(a[i]>b[j]&&max1<dp[i][j])max1=dp[i][j];//看当前DPIJ能否更新MAX1
}//MAX1是本轮不断往后更新用的,记录比a[i]小且最大的dp[i][k](1=<k<j),至关键就是句维护了,不然要扫一次
}//a[i]>b[j]是因为现在要求升序,当前BJ还比AI小,讲明更大的能与AI匹配的BJ(如果有)在后面
int ans = 0; //开始求解,初始化答案是0
for(int i = 1;i<=m;i++) //遍历B串的下标
ans = max(ans,dp[n][i]);//以B[0]为左界,B串每个下标为右界(必须以其结尾)与A整个串匹配的最大长度就是答案
printf("%d\n",ans); //输出答案
}
return 0;
}
/*
Sample Input
1
9 1 4 2 6 3 8 5 9 1
6 2 7 6 3 5 1
Sample Output
3
*/