洛谷 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;
}
唯愿,青春不辜负梦想,未来星辰闪耀