排队照相

题目描述

有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;
}

  • 22
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值