多米诺骨牌放置问题(状压DP)
例题:
最近小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)的更多相关文章
- poj 2411 Mondriaan's Dream 骨牌铺放 状压dp
题目链接 题意 用\(1\times 2\)的骨牌铺满\(H\times W(H,W\leq 11)\)的网格,问方案数. 思路 参考focus_best. 竖着的骨牌用\(\begin{pmatri ...
- 省选训练赛第4场D题(多米诺骨牌)
题目来自FZU2163 多米诺骨牌 Time Limit: 1000 mSec Memory Limit : 32768 KB Problem Description Vasya很喜欢排多米诺 ...
- P1282 多米诺骨牌
P1282 多米诺骨牌 题目描述 多米诺骨牌有上下2个方块组成,每个方块中有1~6个点.现有排成行的 上方块中点数之和记为S1,下方块中点数之和记为S2,它们的差为|S1-S2|.例如在图8-1中,S ...
- P1282 多米诺骨牌[可行性01背包]
题目来源:洛谷 题目描述 多米诺骨牌有上下2个方块组成,每个方块中有1~6个点.现有排成行的 上方块中点数之和记为S1,下方块中点数之和记为S2,它们的差为|S1-S2|.例如在图8-1中,S1=6+ ...
- 【Tsinghua OJ】多米诺骨牌(domino)问题
(domino.c/cpp)[问题描述] 小牛牛对多米诺骨牌有很大兴趣,然而她的骨牌比较特别,只有黑色和白色的两种.她觉 得如果存在连续三个骨牌是同一种颜色,那么这个骨牌排列便是不美观的.现在她有n个 ...
- 【01背包】洛谷P1282多米诺骨牌
题目描述 多米诺骨牌有上下2个方块组成,每个方块中有1~6个点.现有排成行的 上方块中点数之和记为S1,下方块中点数之和记为S2,它们的差为|S1-S2|.例如在图8-1中,S1=6+1+1+1=9, ...
- P1282 多米诺骨牌 (背包变形问题)
题目描述 多米诺骨牌有上下2个方块组成,每个方块中有1~6个点.现有排成行的 上方块中点数之和记为S1,下方块中点数之和记为S2,它们的差为|S1-S2|.例如在图8-1中,S1=6+1+1+1=9, ...
- [LeetCode] Push Dominoes 推多米诺骨牌
There are N dominoes in a line, and we place each domino vertically upright. In the beginning, we si ...
- [Luogu1282]多米诺骨牌(DP)
#\(\color{red}{\mathcal{Description}}\) \(Link\) 我们有一堆多米诺骨牌,上下两个部分都有点数,\(But\)我们有一个操作是可以对调上下的点数.若记一块 ...
随机推荐
- qt中的tcp编程
server .server.h #define DIALOG_H #include <QDialog> #include <QTcpServer> #include < ...
- UEditor工具栏上自定义按钮、图标、事件和右击菜单添加自定义按钮
首先我要说是,举例说的这个版本是1.2以上的,因为一些配置代码转移到了zh-cn.js里,其他没有变化.开门见山直接写:(我自定义的是在线美图功能) 第一步:找到ueditor.config.js文件 ...
- nodejs事件循环
1. 只有一个主线程,node开始执行脚本时,会先进事件循环初始化(同步任务,发出异步请求,规划定时器生效时间,执行promise.nextTick等),这是事件循环还未开始. 2. nodejs每一 ...
- 记录一次CentOS环境升级Python2.6到Python2.7并安装最新版pip
背景介绍 一次实验中需要安装python-etcd包.安装这个包时要求的python和pip版本比目前系统的版本高. 系统是centos6.6 64位 1 2 3 4 5 6 7 [root@m ...
- 分布式代码管理系统Git实践
1.1 版本管理工具介绍 什么是版本管理工具 为什么要使用版本管理工具? 老板要求小明写一个可行性报告,当他写完给老板看后,结果-- Git简介 早先linux内核代码托管在B ...
- vim操作备忘录
vim操作备忘录 vim 备忘录 vim的书籍虽然看不不少,可是老是容易忘记,主要是自己操作总结过少,这个博客就主要用来记录一些比较常见的术语和操作,以防止自己再次忘记. <leader> ...
- 用yii2给app写接口(下)
上一节里我们讲了如何用Yii2搭建一个能够给App提供数据的API后台应用程序.那么今天我们就来探讨下授权认证和通过API接口向服务器提交数据以及如何控制API接口返回那些数据,不能返回那些数据. 授 ...
- 高通ASOC中的codec驱动
ASOC的出现是为了让codec独立于CPU,减少和CPU之间的耦合,这样同一个codec驱动就无需修改就可以匹配任何一款平台. 在Machine中已经知道,snd_soc_dai_link结构就指明 ...
- CodeForces-748C
这题就是确定一个点,然后去找能够实现最短距离的点且距离最远的点,因为题目要求点最少.在查找时,如果从最后的点开始枚举,找到的第一个满足距离最短的点一定是最远点,但是查找的复杂度是O(n),共有n次查找 ...
- Appium疑难杂症
坑之初体验 在Appium的初体验中,遇到了一些坑坑洼洼.将他们记录下来,以后方便查阅. 1. session大于60秒没接收到命令自动关闭 通过Appium-Python-Client连接到appi ...