例题:

最近小A遇到了一个很有趣的问题:
现在有一个\(n\times m\)规格的桌面,我们希望用\(1 \times 2\)规格的多米诺骨牌将其覆盖。
例如,对于一个\(10 \times 11\)的桌面,下面为一种合法覆盖方案:

那么给定n、m,应该如何覆盖呢?
但是小A并不满足于覆盖桌面,他希望知道能够覆盖整个桌面的合法方案数。
输入有 \(t\) 组数据,对于每组数据:
输入\(n、m\),输出合法方案数\(ans\),答案\(ans\)对\(10^9+7\)取模,每个答案占一行。
以输入两个\(0\)为终止标志,输入保证\(n \times m\)为偶数 , \(t \leq 5\)。
***

子任务:

数据范围1 : \(n、m\leq 4\)

咳咳咳.... 滑稽吧。
直接暴力DFS一下即可。
复杂度:\(O(?)\)
***

数据范围2 : \(n、m \leq 10\)

思路解析:

以每一行来状压DP,假设\(m \leq n\):
用一个二进制数表示每一行的状态,一共有: \(2^m\) 种,1代表放了,0代表没放。
那么每次枚举上一次的状态\(k\),枚举这一次的状态\(j\),然后检查一下是否可以转移。
如果可以,直接\(f[i][j] = f[i][j] + f[i-1][k];\)
可以转移的条件 自己yy ,考虑三种情况:不放,横放,竖放。
一个小技巧:竖放的情况只在后面那一格填1,保证合法。
时间复杂度\(O(t \times n \times {(2^m)}^2)\)

实现代码:

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

long long f[13][2048]; int n,m,mx;

bool check(int s,int t){
    int r = 0,g = 0;
    for(int i = 0; i < m; i ++)
        r |= ((s&(1<<i)) ? 0:(1<<i));
    if((r&t) != r)return false;
    r = 0;
    for(int i = 0; i <= m; i ++){
        if(((1<<i)&s) && ((1<<i)&t))g = (g+1)%2;
        else if(g)return false;
    }
    return true;
}

int main(){
    while(scanf("%d %d\n",&n,&m)){
        if(!n && !m)break;
        if(n < m)swap(n , m);
        memset(f,0,sizeof(f));
        mx = (1<<m) - 1;
        f[0][mx] = 1;
        for(int i = 1; i <= n; i ++)
            for(int j = 0; j <= mx; j ++)
                for(int k = 0; k <= mx; k ++)
                    if(check(k,j))f[i][j] += f[i-1][k];
        cout<<f[n][mx]<<endl;
    }return 0;
}

数据范围3: \(n、m \leq 15\)

思路解析:

显然上一种状压\(DP\)会超时
以每一格来状压DP,假设\(m \leq n\),
把棋盘从上往下,从左往右分为\(n \times m\)个阶段,每一个格子对应一个。
那么把轮廓线压入状态中,一共有\(2^m\)种状态。
如图:

图中黄色格子为当前决策格,他有两种决策,放或者不放。
那么当前枚举的历史状态\(k\)为{ \(k_4k_3k_2k_1k_0\) }
那么对应的三种放置方法:
不放:新状态为{ \(k_3k_2k_1k_00\) } , 条件为\(k4=1\)
上放:新状态为{ \(k_3k_2k_1k_01\) } , 条件为\(k4=0\)并且当前不是第一行。
右放:新状态为{ \(k_3k_2k_111\) } , 条件为\(k4-1\)并且当前不是第一列。
还不懂得怎么设状态的可以去看刘汝佳(ORZ)的训练指南(蓝书)的P384-386。
时间复杂度:\(O(t \times n \times m \times 2^m)\)

实现代码:

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;

long long f[2][2048]; int cur,n,m,mx;

int main(){
    freopen("testdate.in","r",stdin);
    while(scanf("%d %d\n",&n,&m)){
        if(!n && !m)break;
        if(n < m)swap(n , m);
        mx = (1<<m) - 1; cur = 0;
        memset(f,0,sizeof(f));
        f[cur][mx] = 1;
        for(int i = 1; i <= n; i ++)
            for(int j = 0; j < m; j ++){
                cur^=1;
                memset(f[cur],0,sizeof(f[cur]));
                //自上往下三种转移分别为:不放、上方、右放。
                for(int k = 0; k <= mx; k ++){
                    if(k & (1<<(m-1))){
                        f[cur][(k<<1)^(1<<m)] += f[cur^1][k];
                        if(j && !(k&1))f[cur][((k<<1)|3)^(1<<m)] += f[cur^1][k];
                    }
                    else if(i)f[cur][(k<<1)|1] += f[cur^1][k];
                }

            }
        printf("%lld\n",f[cur][mx]);
    }return 0;
}

多米诺骨牌放置问题(状压DP)的更多相关文章

  1. poj 2411 Mondriaan's Dream 骨牌铺放 状压dp

    题目链接 题意 用\(1\times 2\)的骨牌铺满\(H\times W(H,W\leq 11)\)的网格,问方案数. 思路 参考focus_best. 竖着的骨牌用\(\begin{pmatri ...

  2. 省选训练赛第4场D题(多米诺骨牌)

    题目来自FZU2163 多米诺骨牌 Time Limit: 1000 mSec    Memory Limit : 32768 KB  Problem Description Vasya很喜欢排多米诺 ...

  3. P1282 多米诺骨牌

    P1282 多米诺骨牌 题目描述 多米诺骨牌有上下2个方块组成,每个方块中有1~6个点.现有排成行的 上方块中点数之和记为S1,下方块中点数之和记为S2,它们的差为|S1-S2|.例如在图8-1中,S ...

  4. P1282 多米诺骨牌[可行性01背包]

    题目来源:洛谷 题目描述 多米诺骨牌有上下2个方块组成,每个方块中有1~6个点.现有排成行的 上方块中点数之和记为S1,下方块中点数之和记为S2,它们的差为|S1-S2|.例如在图8-1中,S1=6+ ...

  5. 【Tsinghua OJ】多米诺骨牌(domino)问题

    (domino.c/cpp)[问题描述] 小牛牛对多米诺骨牌有很大兴趣,然而她的骨牌比较特别,只有黑色和白色的两种.她觉 得如果存在连续三个骨牌是同一种颜色,那么这个骨牌排列便是不美观的.现在她有n个 ...

  6. 【01背包】洛谷P1282多米诺骨牌

    题目描述 多米诺骨牌有上下2个方块组成,每个方块中有1~6个点.现有排成行的 上方块中点数之和记为S1,下方块中点数之和记为S2,它们的差为|S1-S2|.例如在图8-1中,S1=6+1+1+1=9, ...

  7. P1282 多米诺骨牌 (背包变形问题)

    题目描述 多米诺骨牌有上下2个方块组成,每个方块中有1~6个点.现有排成行的 上方块中点数之和记为S1,下方块中点数之和记为S2,它们的差为|S1-S2|.例如在图8-1中,S1=6+1+1+1=9, ...

  8. [LeetCode] Push Dominoes 推多米诺骨牌

    There are N dominoes in a line, and we place each domino vertically upright. In the beginning, we si ...

  9. [Luogu1282]多米诺骨牌(DP)

    #\(\color{red}{\mathcal{Description}}\) \(Link\) 我们有一堆多米诺骨牌,上下两个部分都有点数,\(But\)我们有一个操作是可以对调上下的点数.若记一块 ...

随机推荐

  1. qt中的tcp编程

    server .server.h #define DIALOG_H #include <QDialog> #include <QTcpServer> #include < ...

  2. UEditor工具栏上自定义按钮、图标、事件和右击菜单添加自定义按钮

    首先我要说是,举例说的这个版本是1.2以上的,因为一些配置代码转移到了zh-cn.js里,其他没有变化.开门见山直接写:(我自定义的是在线美图功能) 第一步:找到ueditor.config.js文件 ...

  3. nodejs事件循环

    1. 只有一个主线程,node开始执行脚本时,会先进事件循环初始化(同步任务,发出异步请求,规划定时器生效时间,执行promise.nextTick等),这是事件循环还未开始. 2. nodejs每一 ...

  4. 记录一次CentOS环境升级Python2.6到Python2.7并安装最新版pip

    背景介绍 一次实验中需要安装python-etcd包.安装这个包时要求的python和pip版本比目前系统的版本高. 系统是centos6.6    64位 1 2 3 4 5 6 7 [root@m ...

  5. 分布式代码管理系统Git实践

    1.1 版本管理工具介绍   什么是版本管理工具   为什么要使用版本管理工具?   老板要求小明写一个可行性报告,当他写完给老板看后,结果--     Git简介   早先linux内核代码托管在B ...

  6. vim操作备忘录

    vim操作备忘录 vim 备忘录 vim的书籍虽然看不不少,可是老是容易忘记,主要是自己操作总结过少,这个博客就主要用来记录一些比较常见的术语和操作,以防止自己再次忘记. <leader> ...

  7. 用yii2给app写接口(下)

    上一节里我们讲了如何用Yii2搭建一个能够给App提供数据的API后台应用程序.那么今天我们就来探讨下授权认证和通过API接口向服务器提交数据以及如何控制API接口返回那些数据,不能返回那些数据. 授 ...

  8. 高通ASOC中的codec驱动

    ASOC的出现是为了让codec独立于CPU,减少和CPU之间的耦合,这样同一个codec驱动就无需修改就可以匹配任何一款平台. 在Machine中已经知道,snd_soc_dai_link结构就指明 ...

  9. CodeForces-748C

    这题就是确定一个点,然后去找能够实现最短距离的点且距离最远的点,因为题目要求点最少.在查找时,如果从最后的点开始枚举,找到的第一个满足距离最短的点一定是最远点,但是查找的复杂度是O(n),共有n次查找 ...

  10. Appium疑难杂症

    坑之初体验 在Appium的初体验中,遇到了一些坑坑洼洼.将他们记录下来,以后方便查阅. 1. session大于60秒没接收到命令自动关闭 通过Appium-Python-Client连接到appi ...