2021年度训练联盟热身训练赛第五场 G-Human Pyramid 动态规划

链接:牛客网

题目

The Barefooted Acrobatics People’s Club wants to make a group photo in an original way. For the photo, they want to make a human pyramid, where each person rests on the ground or rests on the shoulders of two people below him or her.

Making a human pyramid demands a lot from the acrobats involved, so the club selected a group consisting of strong people of which they are assured that these people can carry enough weight. The others are ‘agile’ and to make sure everyone is comfortable during the photo, there can only be agile people directly above an agile person.

The photographer wants to make a photo of a pyramid with h people on the flfloor, h−1 on the second layer, h−2 on the third layer, and so on, with a single person on the hth layer. You have s strong people at your disposal, and the other h(h + 1) − s people are agile. What is the number of ways you can arrange the pyramid satisfying the demands of the photographer? Since this number may be large, you should fifind it modulo 109 + 7.

Two pyramids P1 and P2 are difffferent if there exists a location where P1 has an agile person and P2 a strong person, or vice versa.
输入描述:
The input consists of:
A line containing two integers h (1 ≤ h ≤ 100) and s (0 ≤ s ≤ h(h + 1)), the number of layers in the pyramid and the number of strong people.
输出描述:
Output the number of possible ways to build a pyramid with the given constraints, modulo109 + 7.
示例1
输入

3 3

输出

3

示例2
输入

5 3

输出

14

题意

由人搭起一个人型金字塔,人分为两种,一种是普通人(用N表示),一种是强壮的人(用S表示),S只能站立在地面上或者站在同为S的人上面(即P要么在地面,要么左右侧均有S),给定你金字塔的高度,S的数量,求能够构成的金字塔的种数。

思路

单纯的暴力显然是会T的,考虑用dp解决。
一种可能
我们可以观察到一个S要么在地面,要么需要左右两侧S的支撑,如果我们从斜对角的方向观察题目,思路就更加清晰了
在这里插入图片描述
如果第i行对角线有j(j>1)个S的话,那么这j个S必定处于对角线底部连续,且其前一行至少有j-1个S以对其进行支撑
另一种情况就是第i行只有1个S,S站在地面上

dp

我们定义dp数组的含义如下
后面定义的最后一行都指斜对角线最后一行(以左上至右下,从左至右标号)

在这里插入图片描述
转移方程为
dp[i][j][k]=dp[i][j][k+1]
(1)+dp[i-1][j-k][k-1] (2)
解释:
最后一行至少使用k个S有两种可能:
1.最后一行至少使用k+1个S
2.最后一行正好使用k个S

第二种可能:
如果要让最后一行能够正好有k个人,那么其前一行需要至少有k-1个人,且从第1行至倒数第二行总共有j-k个S

代码

#include <iostream>
#define ll long long
#define f(x,y,z) for(int x=(y),_=(z);x<_;x++)
using namespace std;
ll dp[100+10][5000+300][100+10];
const int modn=1e9+7;
void solve() {
	int h,s;
	cin >> h >> s;
	f(i,0,h) {
		f(j,0,h) {
			dp[i][0][j]=0;
		}
	}
	f(i,1,h+1) {
		f(j,1,s+1) {
			for(int k=min(i,j);k>=0;k--) {
				if(j==0) {
					continue;
				}
				if(k==0) {
					dp[i][j][k]=(dp[i][j][k+1]+dp[i-1][j-k][0])%modn;
					continue;
				}
				if(k==j) {
					if(j==1) {
						dp[i][j][k]=1;
					}
					else {
						dp[i][j][k]=0;
					}
					continue;
				}
				dp[i][j][k]=(dp[i][j][k+1]+dp[i-1][j-k][k-1])%modn;
			}
		}
	}
	printf("%lld\n",max(dp[h][s][0],1LL));
}
int main(void) {
	solve();
}




  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ljw0925

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值