题目描述
有 K 种不同规则的长方体砖块,长宽高分别是:
1×1×1 、 2×1×1 、 3×1×1 …, K×1×1 。还给出一个 W×1×1 的地基,如下图所示, W =9,k =3,下面的是地基:
现在你要在地基上堆放砖块,必须满足如下的规则:
- 砖块只能横放,不能竖放。
- 砖块必须放置在整数位置,且不能越出地基。
- 砖块任何部分的正下方都必须要有其他砖块或者是地基。
例如:下图是不合法的放置方式,有 4 个不合法的地方:
我们定义一种堆放砖块方案的高度 height :它是指该方案中最高的砖块是第几层,其中地基是第 0 层,例如(图二)的高度是 3。
我们定义不同的堆放方案:例如有两种堆放方案 A 和 B,只要满足两个条件之一,方案 A 和方案 B 就是不同的方案:
1、 在某个位置方案 A 有砖块而方案B在该位置没有砖块,或者方案 B 有砖块而方案 A 没有。
2、 在某个位置方案 A 和方案 B 都有砖块,但是它们不是同一种规格的砖块。给定地基的长度 W ,和地砖的最大长度
K ,你的任务是计算有多少种不同的堆放砖块的方案,你的堆放砖块方案的高度 height 不能超过给定的 H 。答案模 1000000007。
例如:下图是W=3 , k=3 , H=2 的所有合法方案:
输入格式 1710.in
多组测试数据。
第一行,一个整数 G ,表示有G 组测试数据。 1≤G≤3 。
每组测试数据格式如下:
一行,三个整数 W 、H 、 K 。1≤W,H≤50 。 1≤K≤W 。
输出格式 1710.out
共 G 行,每行一个整数。
输入样例 1710.in
3
3 1 3
3 2 3
10 10 3
输出样例 1710.out
13
83
288535435
看到不同的方案,我想到了状态压缩,也许可以写个搜索试试看。虽然加了些剪枝,但是还是 TLE 了。于是又想到用 dp 做,但是状态却不知道如何表示才恰当。
最终还是在 lgj 的评讲之后才明白了。这题原来是个 dp 套 dp ,还是子问题的思想。
定义
以上图为例,当前宽度为
i
,高度为
有一个问题,为什么左边部分的宽度取的不是 i−L 而是 i−L−1 呢?原因:强行用一个空的位置隔开,不会取重复。什么意思呢?其实,紧挨着而中间没有空隙的方案,会包含在接下来讨论的子问题当中。
再来说,上面的为什么
L
不取到
也就是说,有一种情况是需要单独讨论的。当
现在我们只剩最后一个问题了,如何求出 c[] 数组?其实这个是比较简单的。
- 当 L=1 1时,显然只有 1 种方案;
- 当 L=2 时,可以用一块长度为 2 的大砖块,或者从 L=1 的基础上再加一块长度为 1 的砖块;
- 当 L=3 时,可以用一块长度为 3 的砖块,或者从 L=2 的基础上再加一块长度为 1 的砖块,或者从 L=1 的基础上再加一块长度为 2 的砖块;
- …………
- 注意当 L=k+2 时,有砖块长度限制,前一块可取的范围就要从2开始了。
由上述,可得
参考代码:
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
using namespace std;
const int maxn = 55;
const int mod = 1e9 + 7;
long long c[maxn];
long long dp[maxn][maxn];
int main(void) {
freopen("1710.in", "r", stdin);
freopen("1710.out", "w", stdout);
int g;
scanf("%d", &g);
while (g--) {
int w, h, k;
scanf("%d%d%d", &w, &h, &k);
memset(c, 0, sizeof c);
c[0] = 1;
for (int i = 1; i <= w; i++)
for (int j = 1; j <= min(i, k); j++) (c[i] += c[i - j]) %= mod;
memset(dp, 0, sizeof dp);
for (int i = 0; i <= w; i++) dp[i][0] = 1;
for (int i = 0; i <= h; i++) dp[0][i] = 1;
for (int i = 1; i <= w; i++)
for (int j = 1; j <= h; j++) {
for (int l = 0; l < i; l++)
(dp[i][j] += (dp[i - l - 1][j] * dp[l][j - 1]) % mod * c[l] % mod) %= mod;
(dp[i][j] += dp[i][j - 1] * c[i]) %= mod;
}
printf("%I64d\n", dp[w][h]);
}
return 0;
}