洛谷 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 @   Mystery_Sky  阅读(108)  评论(0编辑  收藏  举报
编辑推荐:
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· 一次Java后端服务间歇性响应慢的问题排查记录
· dotnet 源代码生成器分析器入门
· ASP.NET Core 模型验证消息的本地化新姿势
· 对象命名为何需要避免'-er'和'-or'后缀
阅读排行:
· 编程神器Trae:当我用上后,才知道自己的创造力被低估了多少
· 开发的设计和重构,为开发效率服务
· 从零开始开发一个 MCP Server!
· Ai满嘴顺口溜,想考研?浪费我几个小时
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
点击右上角即可分享
微信分享提示