合唱队——区间dp
P3205 [HNOI2010]合唱队 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
一道刚开始把我唬住的题。看完题解发现,emmmmm也没那么难。
每个人进入队伍里,只有2种可能,1种是从左边加入,另外1种是从右边进入,所以我们的状态表示是有3个数
f[i][j][0]表示的是第i人从左边进来的方案数量
f[i][j][1]表示的是第j人从右边进来的方案数量
状态计算:
从左边进来肯定前1个人比他高,前1个人有2种情况,要么在i+1号位置,要么在j号位置。
同理
从右边进来肯定前1个人比他矮,前1个人有2种情况,要么在j-1号位置,要么在i号位置。
最终求的是f[1][n][1]+f[1][n][0]的值
初始化f[1][n][0]=1,不要把右边也初始化为1.这样如果只有一个人的话,最终方案就有两个值了,那就错了。所以咱们默认第一个人从左边进来。
但是看完题解,我不明白为什么f[1][n][1]+f[1][n][0]就可以表示题中他希望的完美排列。后来我仔细思考了一下,因为完美排列就是这n个数经过不同的排列方式最终得到的,使得最终排列是1到n从左到右排列出来的。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int N=1e3+100,mod=19650827; 4 int a[N],f[N][N][2]; 5 6 int main() 7 { 8 int n;scanf("%d",&n); 9 for(int i=1;i<=n;i++)scanf("%d",&a[i]); 10 for(int i=1;i<=n;i++)f[i][i][0]=1; 11 for(int len=1;len<=n;len++) 12 { 13 for(int i=1;i+len-1<=n;i++) 14 { 15 int j=i+len-1; 16 if(a[i]<a[i+1])f[i][j][0]+=f[i+1][j][0]; 17 if(a[i]<a[j])f[i][j][0]+=f[i+1][j][1]; 18 if(a[j]>a[i])f[i][j][1]+=f[i][j-1][0]; 19 if(a[j]>a[j-1])f[i][j][1]+=f[i][j-1][1]; 20 f[i][j][0]%=mod; 21 f[i][j][1]%=mod; 22 } 23 } 24 25 int ans=(f[1][n][0]+f[1][n][1])%mod; 26 printf("%d\n",ans); 27 28 29 30 return 0; 31 }