主要思路:
课上的例题,得到转移方程以后便很好实现了(这也是动态规划的特点)
LIS :
记dp[ i ]表示以第i个元素结尾的LIS(注意,这里的定义意味着第i个元素是一定被包括进去的),所以dp[i]的值至少是1,这也是合情合理的
此时,我们需要检验第i个元素是否能够插入到之前的某个LIS中去,所以我们遍历 i 之前的 i-1 个元素,比较第i个元素和当前遍历到的元素的大小,更新 dp[ i ] = max{ dp[ j ]+1 } for j in [ 1, i-1 ]
需要循环n*(n-1)/2次,复杂度:O(n2)
LCS :
记dp[ i ] [ j ]表示 A序列的前 i 个元素LCS长度、 B序列的前 j 个元素LCS长度
转移方程:
当 a[ i ] == b[ j ]时,dp[ i ] [ j ] = dp[ i-1 ] [ j-1 ]+1
否则 dp[ i ] [ j ] = max{ dp[ i-1 ] [ j ], dp[ i ] [ j-1 ] }
即,遇到了相同的数字,满足题意即可更新答案+1
B:LIS & LCS
东东有两个序列A和B。
他想要知道序列A的LIS和序列AB的LCS的长度。
注意,LIS为严格递增的,即a1<a2<…<ak(ai<=1,000,000,000)。
Input
第一行两个数n,m(1<=n<=5,000,1<=m<=5,000)
第二行n个数,表示序列A
第三行m个数,表示序列B
Output
输出一行数据ans1和ans2,分别代表序列A的LIS和序列AB的LCS的长度
Sample Input
5 5
1 3 2 5 4
2 4 3 1 5
Sample Output
3 2
A Possible Solution
#include<stdio.h>
const int maxn=5e3+2;
int a[maxn],b[maxn],N,M;
int ans1=0,ans2=0;
int dp1[maxn],dp2[maxn][maxn];
int main(){
scanf("%d %d",&N,&M);
for(int i=1;i<=N;i++)
scanf("%d",a+i);
for(int i=1;i<=M;i++)
scanf("%d",b+i);
dp1[1]=1;
for(int i=2;i<=N;i++){
int tmp=0;
for(int j=1;j<i;j++){
if(a[j]>=a[i])continue;
tmp=tmp<dp1[j]?dp1[j]:tmp;
}
dp1[i]=tmp+1;
}
for(int i=1;i<=N;i++)
ans1=ans1<dp1[i]?dp1[i]:ans1;
/**********分割线***********/
dp2[0][0]=0,dp2[1][0]=0,dp2[0][1]=0;
for(int i=1;i<=N;i++){
for(int j=1;j<=M;j++){
if(a[i]==b[j])dp2[i][j]=dp2[i-1][j-1]+1;
else{
dp2[i][j]=dp2[i-1][j]>dp2[i][j-1]?dp2[i-1][j]:dp2[i][j-1];
}
}
}
ans2=dp2[N][M];
printf("%d %d\n",ans1,ans2);
return 0;
}