动态规划:铺砖问题

问题描述:给定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]);
}
原文地址:https://www.cnblogs.com/long98/p/10352222.html