题目描述
有N个身高各不相同(假设身高分别为1...N)的人要排队合影,按照摄影师的要求他们总共要站成R排,其中第i排站N[i]个人,第1排站在最前面,第R排站在最后面。为了照相效果,摄影师还要求每一排从左到右身高递减,每一列从后往前(第R排到第1排)也身高递减。请帮助摄影师计算有多少种满足条件的排队方案。
输入格式
第1行:一个整数R,表示要站成R排(1<=R<=5)。
第2行:R个空格分隔的整数,分别表示第1排到第R排的人数N[1], N[2],...N[R](N[1]>=N[2] >=... >= N[R]);
总人数N<=30。
输出格式
1行:一个整数,表示不同排队的方案数。
输入输出样例
输入样例1:
5
1 1 1 1 1
输出样例1:
1
输入样例2:
3
3 2 1
输出样例2:
16
说明
结果在unsigned int 范围内。
【耗时限制】1000ms 【内存限制】256MB
排队过程中需要满足以下条件:
① 每一行从左往右越来越高(从左往右和从右往左方案数一样)。
② 每一列从前往后越来越高(从后往前越来越矮)。
③ 从前往后每行人数越来越少。
对于给定的总人数N和总排数R,需要决策每一排放多少人,将每一排的人数作为阶段。
R最大为5,可以直接定义5个维度,对于<5的情况后面的用0表示即可。
定义状态:d(i,j,k,l,m)表示1-5排分别站i, j, k, l, m个人的方案数,答案ans = d(n1, n2, n3, n4, n5)。
由于每一行每一列人的身高都是单调的,可以从低到高依次考虑每一个人,对于新加入的任意一个人,只要满足条件③中对每排人数的限制就一定可以满足条件①和条件②。
也就是说,考虑状态转移时并不需要知道前一状态的具体站法,只需要方案数即可。
状态转移方程:考虑状态d(i, j, k, l, m),可以由以下5种状态转移而来:
① d(i-1, j, k, l, m)在第1排增加1个人,需要满足:(i-1 >= j)。
② d(i, j-1, k, l, m)在第2排增加1个人,需要满足:(j-1 >= k)。
③ d(i, j, k-1, l, m)在第3排增加1个人,需要满足:(k-1 >= l)。
④ d(i, j, k, l-1, m)在第4排增加1个人,需要满足:(l-1 >= m)。
⑤ d(i, j, k, l, m-1)在第5排增加1个人,需要满足:(m-1 >= 0)。
由于是求方案 数,所以d(i, j, k, l, m)是上述5种方案的累加和。
初始化:d[0][0][0][0][0] = 1;
以上是思路,下面上代码吧
#include <iostream>
#include <cstdio>
#include <cstring>
#include <sstream>
#include <algorithm>
using namespace std;
const int N=35;
int r;
int s[10];
long long dp[N][N][N][N][N];
int main(){
scanf("%d",&r);
for(int i=1;i<=r;i++) scanf("%d",&s[i]);
dp[0][0][0][0][0]=1;
for(int a=1;a<=s[1];a++)
for(int b=0;b<=a&&b<=s[2];b++)
for(int c=0;c<=b&&c<=s[3];c++)
for(int d=0;d<=c&&d<=s[4];d++)
for(int e=0;e<=d&&e<=s[5];e++){
long long t=dp[a][b][c][d][e];
if(a>b) t+=dp[a-1][b][c][d][e];
if(b>c) t+=dp[a][b-1][c][d][e];
if(c>d) t+=dp[a][b][c-1][d][e];
if(d>e) t+=dp[a][b][c][d-1][e];
if(e>0) t+=dp[a][b][c][d][e-1];
dp[a][b][c][d][e]=t;
}
cout << dp[s[1]][s[2]][s[3]][s[4]][s[5]];
return 0;
}