铺瓷砖 状压dp经典问题

版权声明:若转载请附上原博客链接,谢谢! https://blog.csdn.net/Link_Ray/article/details/88991291

题意

给出 n×m的矩形方块,可以往上面铺1×2的砖块,问铺满这个方块的方案有多少种。

题解

砖块可以竖铺和横铺,竖铺的话会影响到下一行,横铺的话不会影响到下一行,这里我们令 1:表示该方块不会影响下一行,0:表示该方块会影响下一行。

条件一:
考虑(i.j)这一格,如果(i-1,j)上是0,那说明(i-1,j)是竖铺的砖块,那么(i,j)便是1,该点不会影响到下一行了。若(i-1,j)1,那么(i,j)可以是0也可以是1。从而可以得知(i,j) | (i-1,j) = 1

条件二:
只有这个条件还是不够的,因为一旦(i-1,j)是横铺的话,那么(i-1,j+1)也是1,现在考虑初始状态,001100,因为第一行是不会有上一行影响他的,所以如果该点时1那么其必定是横铺。根据横铺的性质,必定有两个连续的1,所以我们可以得出第一行合法的状态有哪些。例如010101便是不合法的。

得出了第一行合法的状态后,枚举第二行的状态,首先要满足的便是条件一,其次考虑条件二,因为第二行是会受上一行影响的,所以如果第二行出现(i,j)出现1那不一定是横铺的砖块,因为如果上一行是(i-1,j)0的话,那么(i,j)已经被覆盖了且不会影响下一行。

仔细观察的话可以发现。如果(i-1,j)1同时(i,j)也是1的话那么该处一定是横铺。所以只需两行状态相与(&)为1的点便是横铺,然后便可以判断是否合法(这里有个优化的地方,便是可以预处理出合法的状态有哪些)。

最终一行的答案必须全是1,因为其没有下一行可以影响了。

代码

#include <bits/stdc++.h>
using namespace std;
const int mod = 1e9+7;
int n,m,mark[1<<11];
int dp[105][1<<11];
void init() {
    for(int i = 0; i < (1<<m); ++i) {
        int k = i;
        bool flag = true;
        while(k) {
            if(k&1) {
                k >>= 1;
                if(!(k&1)) {
                    flag = false;
                    break;
                } 
            }
            k >>= 1;
        }
        if(flag) 
			dp[1][i] = mark[i] = 1;
    }
}
int main() {
    scanf("%d%d", &n, &m);
    if(n < m) swap(n,m);
    init();
    int tot = (1<<m);
    for(int i = 2; i <= n; ++i) {
        for(int j = 0; j < tot; ++j) {
           	for(int k = 0; k < tot; ++k) {
                if((j|k) != tot-1) continue;
                if(!mark[j&k]) continue;
                dp[i][j] = (dp[i][j]+dp[i-1][k])%mod;
            }
        }
    }
    cout << dp[n][tot-1] << endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Link_Ray/article/details/88991291