铺砖问题
给定
n
∗
m
n*m
n∗m的格子,每个格子被染成黑色或白色。现在要用
1
∗
2
1*2
1∗2的砖块覆盖这些格子,要求块与块之间互相不重叠,且覆盖了所有的白色的格子,但不覆盖任意一个黑色格子。求一共有多少种覆盖方法,输出方案数对
M
M
M取余后的结果。
(
1
≤
n
≤
15
,
1
≤
m
≤
5
,
2
≤
M
≤
1
0
9
)
(1\leq n\leq 15,1\leq m\leq 5,2\leq M\leq 10^{9})
(1≤n≤15,1≤m≤5,2≤M≤109)
输入
n = 3
m = 3
...
.x.
...
输出
2
题解
由于黑的的格子不能被覆盖,所以used
对应的格子总是false
。对于白色的格子,如果现在要在(i, j)
位置上放置砖块,那么由于总是从左上方的可放的格子开始放置,因此对于(i', j') < (i, j)
(按字典序比较)的(i', j')
总有used[i'][j'] = true
成立。
此外,由于砖块的大小为1*2
,因此对于每一列j'
在满足
(
i
′
,
j
′
)
≥
(
i
,
j
)
(i',j')\geq (i,j)
(i′,j′)≥(i,j)的所有j'
中,除了最小的i'
之外都满足
u
s
e
d
[
i
′
]
[
j
′
]
=
f
a
l
s
e
used[i'][j']=false
used[i′][j′]=false。因此,不确定的只有每一列里还没查询到的格子中最上面的一个,共m
个。从而可以把这m
个格子的状态压缩编码进行记忆搜索,复杂度为
O
(
n
×
m
×
2
m
)
O(n\times m\times 2^m)
O(n×m×2m)。
int dp[2][1 << maxn];
void solve() {
int *crt = dp[0], *next = dp[1];
crt[0] = 1;
for (int i = n - 1; i >= 0; i--) {
for (int j = m - 1; j >= 0; j--) {
for (int used = 0; used < 1 << m; used++) {
if ((used >> j & 1) || color[i][j]) {
next[used] = crt[used & ~(1 << j)];
} else {
int res = 0;
if (j + 1 < m && !(used >> (j + 1) & 1) && !color[i][j + 1]) {
res += crt[used | 1 << (j + 1)];
}
if (i + 1 < n && !color[i + 1][j]) {
res += crt[used | 1 << j];
}
next[used] = res % M;
}
}
swap(crt, next);
}
}
printf("%d\n", crt[0]);
}