走道铺砖问题

n*m的走道铺满1*2的地砖,求铺设方案数。
1 <= N,M <= 11

状压dp。我们知道这题中上一行的状态可以一定程度上决定下一行,且铺一块砖的方式只有两种:竖放和横放。
不妨用1 1表示横放的砖块,上0下1来表示竖放的砖块。为什么这样表示?

  1. 横放砖块对下一行完全没有影响
  2. 竖放砖块的下半部分填充了下一行的一个格子。
  3. 竖放砖块的上半部分对下一行有影响:如果上一行某一位是0,那么下一行这位只能是1。
  4. 为了保证最后一行没有竖放的砖块,我们只需要保证最后一行都是1。

用dp[i][j]表示第i行状态为j的方案数,那么dp[n][2^m-1]就是答案。
之后就是bottom-up过程了,值得注意的是有许多非法情况需要判断。

  1. 例如第i行第k位已经是0,那么i-1行对应位一定是1,否则非法。如果合法继续检测(i,k+1)。
  2. (i,k)=1,那么继续分类:
    1. (i-1,k)=0,合法,继续检测(i,k+1)。
    2. (i-1,k)=1,则只可能是(i,k+1)=(i-1,k+1)=1,否则非法。如果合法继续检测(i,k+2)。
  3. 对于第一行:
    1. (0,k)=0,继续检测(0,k+1)。
    2. (0,k)=1,则(0,k+1)=1,继续检测(0,k+2)。
  4. 任意需要检测(0,k+2)且k==m-1的情况,都是非法的。

嗯,就这么多。

#include <bits/stdc++.h>
using namespace std;

const int maxrow = 11;
const int maxstat = 1<<11;
int h, w;
long long dp[maxrow][maxstat];

inline bool first_ok(int stat)
{
    for (int i = 0; i < w; )
        if (stat & (1<<i))
        {
            if (i == w-1 || !(stat & (1<<(i+1))) )
                return 0;
            i += 2;
        }else ++i;
    return 1;
}

inline bool judge(int a, int b)
{
    for (int i = 0; i < w; )
    {
        if (!(a & (1<<i)))
        {
            if (!(b & (1<<i))) return 0;
            ++i;
        }else
        {
            if (!(b & (1<<i))) ++i;
            else if (i == w-1 || !(( a & (1<<(i+1)) ) && ( b & (1<<(i+1)) )))
                return 0;
            else i += 2;
        }
    }
    return 1;
}

int main()
{
    while (cin >> h >> w)
    {
        if (!h && !w) break;
        if (w > h) swap(w, h);
        int all = 2 << (w-1);
        memset(dp, 0, sizeof dp);
        for (int i = 0; i < all; ++i)
            if (first_ok(i)) dp[0][i] = 1;
        for (int i = 1; i < h; ++i)
            for (int j = 0; j < all; ++j)
                for (int k = 0; k < all; ++k)
                    if (judge(j, k)) dp[i][j] += dp[i-1][k];
        printf("%lld\n", dp[h-1][all-1]);
    }
    return 0;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值