1. 程式人生 > 其它 >5424. 【NOIP2017提高A組集訓10.25】鳳凰院凶真

5424. 【NOIP2017提高A組集訓10.25】鳳凰院凶真

技術標籤:DP模板

這是一道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(fi1,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; }