算法竞赛进阶指南线性DP——杨老师的照相排列

在这里插入图片描述
排列队形,排出来大致就是这样一个形状。
在这里插入图片描述

怎么排队呢?先枚举判断一下

经过大脑思考,全场最高的人一定在这里。不放在这里,其至少左边或者上边会有一个空位,而那个空位是找不到一个比全场最高的人还高的人去占位的,所以top1 只有这一种选择。
在这里插入图片描述
接下来,看top2放哪?
👇这里可以放
在这里插入图片描述
这里也可以放👇
在这里插入图片描述
但是这里不能放,因为放这后top2 位置的左侧和上面都没有可以安置的人了。
在这里插入图片描述
再来看一个把,在top2 放在第一种情况下时,top3 的选择有两种
在这里插入图片描述
当在top2 放在第二行第一个时,top3 的选择也有两种
在这里插入图片描述
在这,我们或许可以猜到,排队是从身高大到小排,他的上面和左边不能有空的,有空的就意味着我要在(i-1,j)或者(i,j-1)的位置放一个身高更高的,但放到top x时,比他高的都已经排完了,没办法再进行下去,不合法了。

思路

1.按身高排序
2.从上三角开始放,放在哪行都可以,但要求是左和上不能有空的地方,且不能超过边界。
放学生的条件是
在这里插入图片描述
在放第i个人时,不用去管前面的人站在哪里了,只需要将满足条件的方式找出来即可。

只要该学生站在这样一行中,每列学生的身高单调性也就得以满足。换言之,我不需要关心已经站好学生的具体方案。

f (a,b,c,d,e) 定义为从每一排从左起分别站了a,b,c,d,e个学生的方案数
当安排一名新的学生时,其中a,b,c,d,e之一会增加1,从而转移到后续的阶段,符合各维度线性增长的形式。

来了一个新同学后的状态可以为以下几种情况:

  1. 该同学站在第一排,需要满足的条件是 a1<n1,即这一排还没有站满
    f(a+1,b,c,d,e)+=f(a,b,c,d,e);

  2. 该同学站在第二排,需要满足的条件是 a2<n2 并且 他后面的身高要大于它,即a1>a2(第一排有a1个人,第二排有a2个人,a1>a2,第一排的人数多于第二排且中间不留空,说明它的背后肯定安排了人,且身高高于他,满足要求)
    a2<n2 && a1>a2
    f(a,b+1,c,d,e)+=f(a,b,c,d,e);

  3. 第三排,同理
    a3<n3 && a2>a3
    f(a,b,c+1,d,e)+=f(a,b,c,d,e);

  4. 第四排,同理
    a4<n4 && a3>a4
    f(a,b,c,d+1,e)+=f(a,b,c,d,e);

  5. 第五排,同理
    a5<n5 && a4>a5
    f(a,b,c,d,e+1)+=f(a,b,c,d,e);

代码实现

const int N = 34;
LL res[N][N][N][N][N];
int main()
{
    int k;
    while(cin >> k, k)
    {   
        int num[7] = {0};
        memset(res,0,sizeof res);
        for(int i = 1;i <= k;i ++) cin >> num[i];
        res[0][0][0][0][0] = 1;
        for(int a = 0;a <= num[1];a ++)
          for(int b = 0;b <= min(a,num[2]);b ++)
           for(int c = 0;c <= min(b,num[3]);c ++)
             for(int d = 0;d <= min(c,num[4]);d ++)
               for(int e = 0; e <= min(d,num[5]);e ++)
               {
                   LL &x = res[a][b][c][d][e];
                   if(a - 1 >= b) x += res[a - 1][b][c][d][e];
                   if(b - 1 >= c) x += res[a][b - 1][c][d][e];
                   if(c - 1 >= d) x += res[a][b][c - 1][d][e];
                   if(d - 1 >= e) x += res[a][b][c][d - 1][e];
                   x += res[a][b][c][d][e - 1];
               }
        cout << res[num[1]][num[2]][num[3]][num[4]][num[5]] << endl;
    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值