问题描述:给定n*m的格子,每个格子被染成了黑色或者白色。现在要用1*2的砖块覆盖这些格子,要求块与块之间互相不重叠,且覆盖了所有白色的格子,但不覆盖任意一个黑色格子。求一共有多少种覆盖方法,输出方案数对M取余后的结果。
限制条件:
- 1 <= n <= 15
- 1 <= m <= 15
- 2 <= M <= 10^9
样例输入:
n = 3
m = 3
...(.代表白色,x带表黑色)
.x.
...
输出:2
解题思路:
为了不重复统计,我们每次从最左上方的空格处开始放置。对于哪些格子已经被覆盖过了,只需要用一个bool数组来维护就可以了。首先,黑色的格子不能被覆盖,因此used里对应的位置总是false。对于白色的格子,如果要在(i,j)的位置放置砖块,那么由于总是从最左上方的可放的格子开始放置,因此对于(i',j‘) < (i,j)(按字典序比较)的(i',j')总有used[i'][j'] = true成立。
此外,由于砖块的大小为1*2,因此对于每一列j'在满足(i',j')<= (i , j)的所有i‘中,除了最小的i’之外,都满足used[i'][j'] = false。因此,不确定的只有每一列里还没有查询的格子中最上面的一个,共m个。从而可以把m个格子通过状态压缩编码进行记忆话搜索。
代码实例:
int dp[2][1 << MAX_M];
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
",crt[0]);
}