BZOJ 1996: [Hnoi2010]chorus 合唱队(区间dp)
题目:
https://www.lydsy.com/JudgeOnline/problem.php?id=1996
题解:
这题刚拿到手的时候一脸懵逼qwq,经过思考与分析(看题解),发现是一道区间dp
首先我们考虑最终数列的形成过程,可以看做是由一个序列向左右不断加数形成的,因此,就有一个很美妙的性质:对于最终序列的任意一段,最后加入的一定是左端点或者右端点(很显然)。因此我们就考虑到了区间dp。。
最终序列为g,定义dp[i][j][k](k==0||k==1)表示对于最终序列的i-j区间,最后加入的是左端点(g[i])(k==0),还是右端点(g[j])(k==1)。
那么dp方程就很显然了,对于dp[i][j][1](此数放在右边),最后加入的数(g[j])一定比倒数第二个大
(1)倒数第二个数放在了左边,那么if(g[j]>g[i]) dp[i][j][1]+=dp[i][j-1][0];
(2)倒数第二个数放在了右边,那么if(g[j]>g[j-1]) dp[i][j][1]+=dp[i][j-1][1];
对于dp[i][j][0]同理。。。
区间dp最外层枚举区间长度。。(这是一句废话qwq)
p.s.对于初始化时dp[i][i][0/1]只能将0/1其中的一个置成1,否则会导致重复计数。
代码:
#include<bits/stdc++.h> using namespace std; const int p=19650827; const int maxn=1010; int dp[maxn][maxn][2],g[maxn],n; int main(){ scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&g[i]); for(int i=1;i<=n;i++) dp[i][i][0]=1; for(int k=2;k<=n;k++) for(int i=1,j=i+k-1;j<=n;j++,i++){ if(g[j]>g[i]) dp[i][j][1]+=dp[i][j-1][0]; if(g[j]>g[j-1]) dp[i][j][1]+=dp[i][j-1][1]; if(g[i]<g[j]) dp[i][j][0]+=dp[i+1][j][1]; if(g[i]<g[i+1]) dp[i][j][0]+=dp[i+1][j][0]; dp[i][j][0]%=p,dp[i][j][1]%=p; } printf("%d",(dp[1][n][0]+dp[1][n][1])%p); return 0; }