NOIP2018提高组省一冲奖班模测训练3 T2 XYG的蛋糕

9 篇文章 0 订阅
8 篇文章 0 订阅

10月27日NOIP2018提高组省一冲奖班模测训练3

T2 XYG的蛋糕

题目描述

XYG要过生日了,他准备了一个n×m的矩形蛋糕请大家吃。

切蛋糕的方法如下:每次选出已经分出的某一个矩形蛋糕,一刀将其切成两个小矩形蛋糕。当然,这一刀可以选择横切或者竖切。最终XYG要把蛋糕切成n ×m块1×1的小蛋糕。

XYG希望你告诉他总共有多少种不同的切法。两个切法不同当且仅当其中一个切法中存在一条刀痕,在另一个切法中不存在。

输入格式

一行两个整数n, m

输出格式

一行一个整数表示方案数对998244353取模

输入样例

3 2

输出样例

4

数据范围

对于40%的数据,n,m≤15 

对于100%的数据,n,m≤300 


思路

DP

看到这道题,就莫名想起了1 * 2的骨牌覆盖是怎么回事,不过这题确实与1 * 2的骨牌覆盖有类似之处,都是用DP求解,想法很类似

可以发现当矩形切了之后,矩形变小了,就可以利用之前的值 可以设dp[i][j]表示i * j的矩形的方案数 显然那么我们可以枚举切在哪个位置

先考虑横着切,切在k 那么方案数是\sum dp[k][j] * dp[i-k][j],这里用到了乘法原理

同理竖着切 \sum dp[i][k] * dp[i][j-k]

那么根据加法原理,dp[i][j]就是把两个加起来了, 但是这样会对方案数进行重复计算,比如第一刀切第二行,第二刀切第三行,和第一刀切第三行,第二刀切第二行本应是同一种情况,但是这里却计算为两种,因此我们有了下面的这种DP

分别考虑只横切,只竖切,随意切三种情况,dp [i] [j] [1]表示只切横刀的方案数,dp [i] [j] [2]表示只切竖刀的方案数,dp [i] [j] [0]表示所有方案数 ,如果当前是横着切,那么切的这一行以前一定没有横着切过。这样可以保证不重复,其他同理。这里还用到了组合数学中常用的乘法原理和加法原理

  • 加法原理(分类加法计数原理):若事件A有p种产生方法,B有q种,那么事件A或B有p+q种产生方法(A,B的产生方法不能重叠)
  • 乘法原理(分步乘法计数原理):若事件A有p种产生方法,B有q种,那么事件A与B有p+q种产生方法(A,B的产生方法相互独立)

状态设计: dp [i] [j] [1]表示中只切横刀的方案数,dp [i] [j] [2]表示只切竖刀的方案数,dp [i] [j] [0]表示所有方案数

初始化: dp [1] [1] [0] = dp [1] [1] [1] = dp [1] [1] [2] = 0

状态转移: dp[i][j][1] = dp[k][j][2] * dp[i-k][j][0]

                  dp[i][j][2] = dp[i][k][1] * dp[i][j-k][0]

                  dp[i][j][0] = dp[i][j][1] + dp[i][j][2]

复杂度:\Theta \left(n^3 \right )

代码

#include <iostream>
#include <cstdio>

using namespace std;

const int MAXN = 301;
const int MOD = 998244353;

int n, m;
long long dp[MAXN][MAXN][3];

int main ()
{
	scanf ("%d %d", &n, &m);
	dp[1][1][0] = dp[1][1][1] = dp[1][1][2] = 1;
	for (int i = 1; i <= n; ++ i){
		for (int j = 1; j <= m; ++ j){
			if (i == 1 && j == 1) continue;
			for (int k = 1; k < i; ++ k){
				dp[i][j][1] = (dp[i][j][1] + dp[k][j][0] * dp[i - k][j][2] % MOD) % MOD;
			}
			for (int k = 1; k < j; ++ k){
				dp[i][j][2] = (dp[i][j][2] + dp[i][k][0] * dp[i][j - k][1] % MOD) % MOD;
			}
			dp[i][j][0] = (dp[i][j][1] + dp[i][j][2]) % MOD;
		}
	}
	printf ("%lld", dp[n][m][0]);
	return 0;
 } 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值