洛谷 P3205 [HNOI2010]合唱队

题目传送门


思路:

对于理想队形中[l, r]的部分,它可以由第l个人从左边入队,也可以由第r个人从右边入队。

对于第l个人从左边入队的情况,可分为两种:上一个是第l+1个人从左端入队,则需满足h[l] < h[l+1];上一个是第r个人从右端入队,则需满足h[l] < h[r]。

对于第r个人从右边入队的情况则同理。

那么我们可以设f[i][j][k]表示从i到j的方案数,当k=0时,表示上一个入队的是第i个人,当k=1时,表示上一个入队的是第j个人。

根据上面的分析不难写出转移方程:

if(h[i] < h[i+1]) f[i][j][0] += f[i+1][j][0];
if(h[i] < h[j]) f[i][j][0] += f[i+1][j][1];
if(h[i] < h[j]) f[i][j][1] += f[i][j-1][0];
if(h[j] > h[j-1]) f[i][j][1] += f[i][j-1][1];

为了避免重复计算,所以预处理中我们只将所有的f[i][i][0]置为1即可。


Code:

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
//Mystery_Sky
//
#define M 10000100
#define INF 0x3f3f3f3f
#define ll long long
#define Mod 19650827
inline int read()
{
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9') {x=x*10+c-'0';c=getchar();}
	return x*f;
}
int n;
int f[1001][1001][2], h[1001];
int main() {
	n = read();
	for(int i = 1; i <= n; i++) h[i] = read(), f[i][i][0] = 1;
	for(int len = 1; len <= n; len++) {
		for(int i = 1; i + len - 1 <= n; i++) {
			int j = i + len - 1;
			if(h[i] < h[i+1]) f[i][j][0] += f[i+1][j][0];
			if(h[i] < h[j]) f[i][j][0] += f[i+1][j][1];
			if(h[i] < h[j]) f[i][j][1] += f[i][j-1][0];
			if(h[j] > h[j-1]) f[i][j][1] += f[i][j-1][1];
			f[i][j][0] %= Mod, f[i][j][1] %= Mod;
  		}
	}
	printf("%d\n", (f[1][n][0] + f[1][n][1]) % Mod);
	return 0;
} 
posted @ 2019-09-01 20:11  Mystery_Sky  阅读(104)  评论(0编辑  收藏  举报