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

文章讲述了如何使用动态规划方法解决身高排序问题,通过分析队形的放置规则,确定从上三角形开始逐步填充,确保队伍中每个位置的身高单调递减。代码展示了计算给定学生数量可能的队形排列数的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

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

经过大脑思考,全场最高的人一定在这里。不放在这里,其至少左边或者上边会有一个空位,而那个空位是找不到一个比全场最高的人还高的人去占位的,所以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;
}

算法竞赛进阶指南》是一本进阶级别的书籍,不适合初学者阅读。根据引用中的描述,每一章都会总结书中的难点知识,并附上例题和习题。从引用的目录可以看出,《算法竞赛进阶指南》包含了基本算法、位运算、递推与递归、前缀和与差分、二分、排序、倍增、贪心等内容,还包括了基本数据结构如栈、队列、链表、Hash、字符串、Trie、二叉堆等。此外,书中还讲解了动态规划的各种子领域,如线性dp、背包、区间dp、树形dp、状态压缩dp等。对于想要深入学习算法竞赛的读者来说,《算法竞赛进阶指南》是一本很好的参考书籍。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [【算法竞赛进阶指南】学习笔记](https://blog.youkuaiyun.com/cpp_juruo/article/details/122520206)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] - *3* [算法竞赛进阶指南总结(一)](https://blog.youkuaiyun.com/weixin_64393298/article/details/124234703)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值