5424. 【NOIP2017提高A組集訓10.25】鳳凰院凶真
阿新 • • 發佈:2021-01-15
這是一道DP題,然後做的時候發現,DP式子死活推不出來。
題目大意(本人實在是不想複製了呵……
給出A,B序列
找出他們的最長公共嚴格遞增子序列
明確,這是一道DP
所以設狀態
f
i
,
j
f_{i,j}
fi,j表示a序列考慮到i,b序列考慮到j,並且必須選
b
j
b_{j}
bj的最大長度
所以,考慮轉移
f
i
,
j
=
m
a
x
(
f
i
−
1
,
k
)
k
<
j
,
b
k
<
a
j
f_{i,j}=max(f_{i-1,k}) k<j,b_{k}<a_{j}
fi,j=max(fi−1,k)k< j,bk<aj
然後,你會發現一個問題,要輸出路徑的話,還得麻煩一點。
但是,我們還是要看一下怎麼解決。
假設
g
i
,
j
g_{i,j}
gi,j表示a序列考慮到i,b序列考慮到j,的前面那個數。
所以我們就可以遞迴輸出了。
感覺心態都沒了……
所以再講一下細節問題:
我們要知道的是,每個
f
i
,
j
f_{i,j}
fi,j都會存在一個什麼都不選的方案,所以在j迴圈一開始我們就要做出轉移(如下
每次,為了更新這個k,我們需要重新看一下k的定義,作為f[i-1]裡邊最大的那一個,所以要滿足f[i-1][k]<f[i-1][j]&&a[i]>b[j]的條件,我才能轉移k
#include<cstdio>
#include<cstring>
using namespace std;
int n,m,k,a[5005],b[5005],maxx,maxy;
int f[5005][5005],g[5005][5005],ans[5005];
int read()
{
int f=1,x=0;
char s=getchar();
while(s<'0'||s>'9'){if(s=='-') f=-1;s=getchar();}
while(s>='0'&&s<='9') x=x*10+s-'0',s=getchar();
return x*f;
}
void dfs(int x,int y)
{
if(x==0||y==0) return ;
dfs(x-1,g[x][y]);
if(y!=g[x][y]) printf("%d ",b[y]);
}
int main()
{
freopen("okarin.in","r",stdin);
freopen("okarin.out","w",stdout);
n=read();
for(int i=1;i<=n;++i) a[i]=read();
m=read();
for(int i=1;i<=m;++i) b[i]=read();
memset(f,0,sizeof(f)),memset(g,0,sizeof(g));
for(int i=1;i<=n;++i)
{
k=0;
for(int j=1;j<=m;++j)
{
f[i][j]=f[i-1][j],g[i][j]=j;
if(a[i]==b[j])
if(f[i-1][k]+1>f[i][j])
f[i][j]=f[i-1][k]+1,g[i][j]=k;
if(f[i-1][k]<f[i-1][j]&&a[i]>b[j]) k=j;
if(f[i][j]>f[maxx][maxy]) maxx=i,maxy=j;
}
}
printf("%d\n",f[maxx][maxy]);
dfs(maxx,maxy);
return 0;
}