【状态压缩dp】摆骨牌

题目大致意思就是给你1*2的矩形砖块 让你填充满h*w的矩形区域 有多少种填充方法?hw的值小于12, 
两种解法: 
1. 状态压缩dp 

思路很简单,对于每一个格子,我们考虑这个格子上面我们是竖向放砖块还是横向放砖块,如果横向放 我们就有横着的两个11表示,竖向放我们就用竖着的01表示,那么每一行就可以表示为一个状态,在其中一行的时候,首先判断当前行的状态是否符合上一行的要求,即上一行为0的地方这一行这个位置必须是1,上一行为1的地方这一行这个位置就可以随便,然后检查一下当前行是否合法就可以了,合法指的是这一行与上一行的值按位与,得到的值的二进制中的连续1的个数都是偶数个。

如何求相邻两行二进制值的对应关系?可以枚举i-1行的所有二进制值情况,然后判断这个值本身是否合法,如果合法(其实只要这行是中间行所有二进制值都合法的,因为首行我们虚构了第0行为全1序列,最后一行我们只需要全1序列对应的值),再通过它推断出和它兼容的第i行二进制值(和i-1行二进制值兼容的第i行二进制为:第i-1行为0的位,第i行应为1。第i-1行为1的位,第i行为0或1,且如果第i行为1那么表示第i行此时的对应位置是横着放的,应该有偶数个连续的1才合法,只需判断i-1行中为1的位在第i行中如果也为1必须偶数个这样的1相连)。


#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<string>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cstring>

using namespace std;
#define LL long long
bool isRight[1<<13];
bool isOk(int s)
{
    int cnt = 0;
    while(s)
    {
        if(s&1)
        {
            cnt++;
        }
        else
        {
            if(cnt&1)return false;
            cnt = 0;
        }
        s>>=1;
    }
    if(cnt&1)return false;
    return true;
}

int main()
{
    memset(isRight,false,sizeof(isRight));
    for(int i = 0;i<1<<13;i++)
    {
        if(isOk(i))
        {
            isRight[i] = true;
        }
    }
    int h,w;
    while(scanf("%d%d",&h,&w) && h && w)
    {
        LL dp[15][1<<13];
        memset(dp,0,sizeof(dp));
        for(int i = 0;i<(1<<w);i++)
        {
            if(isRight[i])
            {
                dp[0][i] = 1;
            }
        }
        for(int i = 1;i<h;i++)
        {
            for(int j = 0;j<(1<<w);j++)
            {
                for(int k = 0;k<(1<<w);k++)
                {
                    int nk = ((1<<w)-1)^k;
                    if((nk&j) != nk)continue;
                    // 上面两行代码是判断当前状态是否符合上面一行的要求 用位运算 上一行取反 然后异或当前行判断是否相等 判断
                    if(isRight[j&k])
                    {
                        dp[i][j] += dp[i-1][k];
                    }
                }
            }
        }
        //cout << isRight[7] << endl;
        printf("%lld\n",dp[h-1][(1<<w)-1]);
    }
    return 0;
}


转自https://blog.csdn.net/touwangyi/article/details/75309486,还有轮廓线dp解法(这个真的费力去看了....还是没看懂....但我以后一定会花时间来搞懂的,现在时间比较紧)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值