WEEK10 周记 作业——动态规划_LIS & LCS问题
一、题意
1.简述
东东有两个序列A和B。
他想要知道序列A的LIS和序列AB的LCS的长度。
注意,LIS为严格递增的,即a1<a2<…<ak(ai<=1,000,000,000)。
2.输入格式
第一行两个数n,m(1<=n<=5,000,1<=m<=5,000)
第二行n个数,表示序列A
第三行m个数,表示序列B
3.输出格式
输出一行数据ans1和ans2,分别代表序列A的LIS和序列AB的LCS的长度
4.样例
Input
5 5
1 3 2 5 4
2 4 3 1 5
Output
3 2
二、算法
主要思路
LIS 最长上升子序列
给定 n 个整数 Ai,A2,…,AnA_i, A_2,…, A_nAi,A2,…,An,按从左到右的顺序选出尽量多的整数,组成一个上升子序列。输出最长上升子序列的长度。
例如,序列 1,6,2,3,7,5上升子序列可以是1,6,7,;也可以是 1,2,3,5。而 最长上升子序列为 1,2,3,5,其长度为 4,故 ans = 4
求解其长度的状态转移方程为:
f1=1f_1=1f1=1fi=max{fj∣j<i⋀Aj<Ai}+1,i≥2fi=max\{f_j|j<i \bigwedge A_j<A_i\}+1,i\ge2fi=max{fj∣j<i⋀Aj<Ai}+1,i≥2
那么最终的LIS的长度为maxf[i],i=1…nmax{f[i], i=1…n}maxf[i],i=1…n
算法时间复杂度为O(n2)O(n^2)O(n2)
LCS 最长公共子序列
给两个序列 A 和 B,求长度最大的公共子序列的长度
例如:
A – 1,5,2,6,8,7
B – 2,3,5,6,9,8,4
最长公共子序列为:
A – 1,5,2,6,8,7
B – 2,3,5,6,9,8,4
或
A – 1,5,2,6,8,7
B – 2,3,5,6,9,8,4
设f[i][j]f[i][j]f[i][j]为A1,A2,…,AiA_1, A_2, …, A_iA1,A2,…,Ai 和 B1,B2,…,BjB_1, B_2, …, B_jB1,B2,…,Bj 的 LCS 长度
状态转移方程f[1][0]=f[0][1]=f[0][0]=0f[1][0] = f[0][1] = f[0][0] = 0f[1][0]=f[0][1]=f[0][0]=0f[i][j]=f[i−1][j−1]+1,Ai=Bjf[i][j] = f[i-1][j-1] + 1,Ai = Bjf[i][j]=f[i−1][j−1]+1,Ai=Bjf[i][j]=max(f[i−1][j],f[i][j−1]),Ai≠Bjf[i][j] = max(f[i-1][j], f[i][j-1]),A_i\ne B_jf[i][j]=max(f[i−1][j],f[i][j−1]),Ai=Bj
输出答案:f[n][m]f[n][m]f[n][m]
时间复杂度:O(nm)O(nm)O(nm)
三、代码
#include<iostream>
#include<cstring>
#include<string>
using namespace std;
int a[5010]; //从1开始
int b[5010]; //从1开始
int n,m;
int ans1,ans2;
int lth1[5010]; //从1开始
int f[5010][5010]; //从1,1开始
int main(){
f[0][0] = 0;
f[0][1] = 0;
f[1][0] = 0;
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]);
//求ans1
for(int i=1;i<=n;i++){
lth1[i] = 1;
for(int j=1;j<i;j++){
if(a[j]<a[i]&<h1[i]<lth1[j]+1) lth1[i] = lth1[j]+1;
}
}
for(int i=1;i<=n;i++)
if(lth1[i]>ans1) ans1 = lth1[i];
//求ans2
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(a[i]==b[j]) f[i][j] = f[i-1][j-1]+1;
else{
f[i][j] = f[i-1][j]>f[i][j-1]?f[i-1][j]:f[i][j-1];
}
}
}
ans2 = f[n][m];
printf("%d %d",ans1,ans2);
return 0;
}