+传送门+
给定 n,m n , m ,并给定长度分别为 n,m n , m 的两个数组 a[],b[] a [ ] , b [ ] ,输出 a,b a , b 数组的最长公共子序列的长度和它本身。
n,m(1 ≤n,m ≤ 500) n , m ( 1 ≤ n , m ≤ 500 )
听说 O(n4) O ( n 4 ) 都能过
(如果有兴趣的话可以回忆一下更基础更经典的类似题)
最长上升子序列
最长公共子序列
摆出上面两道题跟这道题还是有一定关系的,但是为了不对大家产生误导,我先谈谈我做这道题的经过。
看到这道题的名字,先瞬间分解成了:
Example
a: a : 7 7 1 1 5 5 6 6 4 4 2 2 7 7
b: b : 7 7 1 1 5 5 4 4 6 6 7 7 2 2
按照递归的取“最长公共子序列”,取出:
7 7 1 1 5 5 6 6 2 2
此序列的“最长上升子序列”为:
1 1 5 5 6 6 (len=3)
但原序列的“最长公共上升子序列”为:
1 1 5 5 6 6 7 7 (len=4)
所以这就不对了。
说句大实话,我真的不知道 O(n4) O ( n 4 ) 怎么写,所以思路从 O(n3) O ( n 3 ) 开始。
我们(根据以往经验)知道这道题中需要两个变量 i,j i , j ( a[],b[] a [ ] , b [ ] 中的下标)
好吧,直接说( dp d p 怎么想出来的貌似不太好说清楚)
f[i][j]: f [ i ] [ j ] : 在 a[1...i] a [ 1... i ] 中以 b[j] b [ j ] 结尾的“最长公共子序列”的长度
1≤i≤n,1≤j≤m,1≤k<j 1 ≤ i ≤ n , 1 ≤ j ≤ m , 1 ≤ k < j
a[i]!=b[j] a [ i ] ! = b [ j ] : i i 之前的以 b[j] b [ j ] 结尾的序列自然没有改变,仍然是长度仍然是 f[i−1][j] f [ i − 1 ] [ j ] ;
a[i]=b[j] a [ i ] = b [ j ] :有一个新的公共元素 b[j] b [ j ] ,把它放到序列尾部,固枚举 k k 求出之前最长的序列并加上去 (b[k]<b[j]) ( b [ k ] < b [ j ] ) 。
差不多了。
(我真是充满诗性)
(Dp后的递归输出如有疑问,请参见 Dp D p 后的递归输出)
#include
#include
using namespace std;
const int MAXN=int(5e2+5);
int l1,l2;
int f[MAXN][MAXN];
int a[MAXN],b[MAXN];
void Print(int x,int y) {
if(f[x][y]==1) {
printf("%d ",b[y]);
return;
}
if(x==0||y==0)
return;
if(f[x][y]==f[x-1][y]) {
Print(x-1,y);
return;
}
for(int i=y-1;i>=1;i--)
if(b[i]1][i]+1) {
Print(x,i);
printf("%d ",b[y]);
return;
}
}
int main()
{
int ans=0,a1,b1;
scanf("%d",&l1);
for(int i=1;i<=l1;i++)
scanf("%d",&a[i]);
scanf("%d",&l2);
for(int i=1;i<=l2;i++)
scanf("%d",&b[i]);
for(int i=1;i<=l1;i++)
for(int j=1;j<=l2;j++) {
if(a[i]!=b[j])
f[i][j]=f[i-1][j];
else {
int Max=0;
for(int k=1;kif(b[k]1][k]);
f[i][j]=Max+1;
if(ansprintf("%d\n",ans);
if(ans)
Print(a1,b1);
}
我们看一下上面那篇代码中,让我们把复杂度升到让人惊恐( 5003 500 3 不太慌)的 O(n3) O ( n 3 ) 是什么。
对对对,除了必要的 i|1≤i≤n,j|1≤j≤m i | 1 ≤ i ≤ n , j | 1 ≤ j ≤ m 的枚举外,还有转移时的枚举 k|1≤k<j k | 1 ≤ k < j 。
所以,考虑从 k k 入手优化。
看一下 k k 都做了什么:枚举 max(f[i−1][1..(j−1)]) m a x ( f [ i − 1 ] [ 1.. ( j − 1 ) ] ) 。
但仔细一想,这是没有必要的,注意下面这两段循环。
#include
#include
using namespace std;
const int MAXN=int(5e2+5);
int l1,l2;
int f[MAXN][MAXN];
int a[MAXN],b[MAXN];
void Print(int x,int y) {
if(f[x][y]==1) {
printf("%d ",b[y]);
return;
}
if(x==0||y==0)
return;
if(f[x][y]==f[x-1][y]) {
Print(x-1,y);
return;
}
for(int i=y-1;i>=1;i--)
if(b[i]1][i]+1) {
Print(x,i);
printf("%d ",b[y]);
return;
}
}
int main()
{
int ans=0,a1,b1;
scanf("%d",&l1);
for(int i=1;i<=l1;i++)
scanf("%d",&a[i]);
scanf("%d",&l2);
for(int i=1;i<=l2;i++)
scanf("%d",&b[i]);
for(int i=1;i<=l1;i++) {
int Max=0;
for(int j=1;j<=l2;j++) {
if(a[i]!=b[j])
f[i][j]=f[i-1][j];
if(a[i]>b[j]&&Max1][j])
Max=f[i-1][j];
if(a[i]==b[j]) {
f[i][j]=Max+1;
if(f[i][j]>ans)
ans=f[i][j],a1=i,b1=j;
}
}
}
printf("%d\n",ans);
if(ans)
Print(a1,b1);
}
O(n2)′ O ( n 2 ) ′
如果你没有戳开上文的 Dp D p 后的递归输出
再给一次机会!!!戳它 → → Dp D p 后的递归输出
好吧,如果你再次选择了拒绝。
下面的代码或许会带来些帮助。
接下来要讲的是
对照上一篇看
#include
#include
#include
using namespace std;
const int MAXN=int(5e2+5);
int l1,l2;
int a[MAXN],b[MAXN];
int tap;
vector<int>ans[MAXN],ans1;
int main()
{
scanf("%d",&l1);
for(int i=1;i<=l1;i++)
scanf("%d",&a[i]);
scanf("%d",&l2);
for(int i=1;i<=l2;i++)
scanf("%d",&b[i]);
for(int j=1;j<=l2;j++) {
ans1.clear();
for(int i=1;i<=l1;i++) {
if(b[j]>a[i]&&ans[i].size()>ans1.size())
ans1=ans[i];
if(b[j]==a[i]) {
ans[i]=ans1;
ans[i].push_back(a[i]);
}
}
}
for(int i=1;i<=l1;i++)
if(tap==0||ans[i].size()>ans[tap].size())
tap=i;
printf("%d\n",ans[tap].size());
for(int i=0;iprintf("%d ",ans[tap][i]);
}
考试的时候,有位同学 O(n4) O ( n 4 ) 比我的 O(n2) O ( n 2 ) 测出来还要快(或许是我太菜),这告诉我们
Thanks T h a n k s for f o r reading r e a d i n g !