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;    
}
原文地址:https://www.cnblogs.com/tang666/p/8744254.html