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;
}
posted @ 2023-12-14 16:38  Nijika  阅读(2)  评论(0)    收藏  举报