luogu P3205 合唱队

银月城传送门

又一道区间的dp。

状态dp[i][j]表示队列中i——j可行的排列方法。

首先定义边界:对于每一个点,dp[i][i]=1(显然)

然后导一下状态转移方程,大区间必然由小区间转移而来,但是转移方法可以有多种。完成区间排列的最后一步,既可以由在左边插入i完成,也可以通过在右边插入j完成。

方便判断,在状态上再加一维,即dp[i][j][0/1],最后一维为0则从左插入i,为1则从右插入j。

每个状态只跟上一个状态有关,但上一个状态并不确定是从左还是右插入,因此需要进行分类讨论:

当右端点的数大于上一个且上一个插入在右端时:f[ i ][ j ][ 1 ] += f[ i ][j - 1][ 1 ];

当右端点的数大于左端点且上一个插入在左端时:f[ i ][ j ][ 1 ] += f[ i ][ j - 1 ][ 0 ];

当左端点的数小于后一个且上一个插入在左端时:f[ i ][ j ][ 0 ] += f[ i + 1 ][ j ][ 0 ];

当左端点的数小于右端点且上一个插入在右端时:f[ i ][ j ][ 0 ] += f[ i + 1 ][ j ][ 1 ];

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int n[1005],dp[1005][1005][2];
int main()
{
    int a;
    cin>>a;
    for(int i=1;i<=a;i++)
    cin>>n[i];
    for(int i=1;i<=a;i++)
    dp[i][i][0]=1;
    for(int l=2;l<=a;l++)
{
    for(int i=1;i<=a-l+1;i++)
    {
        int j=i+l-1;
        if(n[j]>n[j-1])
            dp[i][j][1]+=dp[i][j-1][1];
        if(n[j]>n[i])
            dp[i][j][1]+=dp[i][j-1][0];
        if(n[i]<n[i+1])
            dp[i][j][0]+=dp[i+1][j][0];
        if(n[i]<n[j])
            dp[i][j][0]+=dp[i+1][j][1];
        dp[i][j][0]%=19650827;
        dp[i][j][1]%=19650827; 
    }
}
    cout<<((dp[1][a][0]+dp[1][a][1])%19650827);
    return 0;
}
原文地址:https://www.cnblogs.com/charlesss/p/10338558.html