P3205 [HNOI2010] 合唱队
区间DP
为什么这道题可以用区间dp呢,因为我们如果在对应的位置上人都正确,从小到大,排满全部人即可
思路
平时我们遇见的区间dp题目可能都是一段加另一段,但此题我们想一下,是不是人只能从左或者右添加,所以我们只需考虑左边来的可能,和右边来的可能即可

假设我们要求 $ f[i][j] $的多少可能,是不是需要考虑左边右边来的可能,那左边来的可能是不是要知道上一个添加的人是什么呢?
所以需要记录上一数是左边来还是右边来,因此我们需要开 $ f[n][n][2] $ 分别来记录左右可能

状态转移
if(g[i]<g[i+1]) f[i][j][0] += f[i+1][j][0];
if(g[i]<g[j]) f[i][j][0] += f[i+1][j][1];
if(g[j]>g[i]) f[i][j][1] += f[i][j-1][0];
if(g[j]>g[j-1]) f[i][j][1] += f[i][j-1][1];
初始值
但我们为一个数的时候,直接添加即可,所以设 $ f[i][i][0] $ 为 1,右来也可以
AC代码
#include <bits/stdc++.h>
using namespace std;
int main(){
int f[1005][1005][2]={0},g[1005],n,mod = 19650827;
cin >> n;
for(int i=1;i<=n;i++){
cin >> g[i];
f[i][i][0] = 1;
}
// 0左进 1右进
for(int l=2;l<=n;l++){
for(int i=1;i+l-1<=n;i++){
int j = i+l-1;
if(g[i]<g[i+1]) f[i][j][0] += f[i+1][j][0];
if(g[i]<g[j]) f[i][j][0] += f[i+1][j][1];
if(g[j]>g[i]) f[i][j][1] += f[i][j-1][0];
if(g[j]>g[j-1]) f[i][j][1] += f[i][j-1][1];
f[i][j][0] %= mod;
f[i][j][1] %= mod;
}
}
cout << (f[1][n][0]+f[1][n][1])%mod;
return 0;
}

浙公网安备 33010602011771号