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求解,想法很类似
可以发现当矩形切了之后,矩形变小了,就可以利用之前的值 可以设表示的矩形的方案数 显然那么我们可以枚举切在哪个位置
先考虑横着切,切在k 那么方案数是,这里用到了乘法原理
同理竖着切
那么根据加法原理,就是把两个加起来了, 但是这样会对方案数进行重复计算,比如第一刀切第二行,第二刀切第三行,和第一刀切第三行,第二刀切第二行本应是同一种情况,但是这里却计算为两种,因此我们有了下面的这种DP
分别考虑只横切,只竖切,随意切三种情况,表示只切横刀的方案数,表示只切竖刀的方案数,表示所有方案数 ,如果当前是横着切,那么切的这一行以前一定没有横着切过。这样可以保证不重复,其他同理。这里还用到了组合数学中常用的乘法原理和加法原理
- 加法原理(分类加法计数原理):若事件A有p种产生方法,B有q种,那么事件A或B有p+q种产生方法(A,B的产生方法不能重叠)
- 乘法原理(分步乘法计数原理):若事件A有p种产生方法,B有q种,那么事件A与B有p+q种产生方法(A,B的产生方法相互独立)
状态设计: 表示中只切横刀的方案数,表示只切竖刀的方案数,表示所有方案数
初始化:
状态转移:
复杂度:
代码
#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;
}