Mondriaan‘s Dream(状压DP)

8 篇文章 0 订阅

题目链接

在这里插入图片描述
在这里插入图片描述

思路

有一种1x2的砖块,问有多少种填满的方案。
砖块只能横着放或竖着放,设横着放为1 1,竖着为 1 0 _1^0 10,
显然上下不能有同位的0,并且每一行的连续1的个数减去上一行对应位置的0的个数后不能为奇数,
满足条件的就 d p [ i ] [ j ] + = d p [ i − 1 ] [ k ] dp[i][j]+=dp[i-1][k] dp[i][j]+=dp[i1][k].最后一行一定是填满的,所以答案是 d p [ h ] [ ( 1 < < w ) − 1 ] . dp[h][(1<<w)-1]. dp[h][(1<<w)1].

要让同位没有相同0,那么 j ∣ k 后 每 一 位 都 应 该 是 1 , 所 以 k ∣ j = ( 1 < < w ) − 1 j|k后每一位都应该是1,所以k | j=(1<<w)-1 jk1kj=(1<<w)1。判断减去0后1的奇偶可以令x=j&k,
这样原来的1会变成0,就相当于减去了对应的1。
还有当h,w都为奇数的时候直接输出0就行了,我tle加上这个判断就过了。

代码

#include <iostream>
#include <cstring>
#include <cmath>
#include <bitset>
#include <queue>
#include <vector>
#include <cstdio>
#include <queue>
#include <sstream>
#include <string>
#include <algorithm>
#include <map>
#define rep(i, a, b) for (int i = a; i <= b; i++)
#define reps(i, a, b) for (int i = a; i >= b; i--)
#define mk make_pair
using namespace std;
const int N = 1e2 + 10;
const int M = 17;
const int inf = 0x3f3f3f3f;
const int mod = 1000000007;
typedef long long ll;
ll dp[M][1<<M];
inline int init(int j)
{//第一行没有上一行,所以只要自己能填满并且不冲突就行
    int x=j;
    int cnt=0;
    while (x){
        if(x%2==1)cnt++;
        else{
            if(cnt&1)return 0;
            cnt=0;
        }
        x>>=1;
    }
    if(cnt&1)return 0;
    else return 1;
}
inline bool check(int x,int y)
{//判断减去0对应位置的1后,1的个数的奇偶
    int j=(x&y);
    int cnt=0;
    while (j){
        if(j%2==1)cnt++;
        else{
            if(cnt&1)return 0;
            cnt=0;
        }
        j>>=1;
    }
    if(cnt&1)return 0;
    else return 1;
}
void solve(int h,int w)
{
    if((h&1)&&(w&1)){//全是奇数直接输出0.
        puts("0");
        return;
    }
    int M=(1<<w)-1;
    rep(j,0,M){
        dp[1][j]= init(j);
    }
    rep(i,2,h){
        rep(j,0,M){
            rep(k,0,M){
                if((k|j)!=M)continue;
                if(!check(j,k))continue;
                dp[i][j]+=dp[i-1][k];
            }
        }
    }
    printf("%lld\n",dp[h][M]);
}
int main()
{
    int h,w;
    while (~scanf("%d%d",&h,&w)) {
        if(h==0&&w==0)break;
        memset(dp,0,sizeof dp);
        solve(h, w);
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值