多米诺骨牌放置问题(状压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\)我们有一个操作是可以对调上下的点数.若记一块 ...
随机推荐
- phpstudy 版本切换注意的问题
如果你也在使用phpstudy的话要注意,因为切换版本后,虽然你的phpinfo 但是实际环境用的是系统环境变量 所以你要去改变下环境变量路径,然后重启电脑. 这样你的版本就是你想切换的版本啦!
- Windows Server 2016-抢占FSMO角色
很多情况下,当生产域控制器发生问题无法修复的情况下,我们只能通过抢占FSMO角色以保证用户验证等正常或及时恢复.一般在同一个域环境中,我们往往都会有主备或主辅域控规划,平时工作的时候,两台域控可以实现 ...
- IDEA的导包优化问题
一.现象 文件初始导包状态 package co.x.dw.function; import java.text.SimpleDateFormat; import java.util.ArrayLis ...
- Jenkins实现PHP的自动部署
1.汉化jenkins 1).安装汉化包 系统管理 -> 插件管理 -> 安装插件 ->选择插件(Locale plugin) 2).设置语言为中文 系统管理 -> 系统设置 ...
- 在tableViewCell的点击事件中处理界面跳转问题
UIViewController *controller; UIView *view = self.view; while (1) { controller = (UIViewController * ...
- 解决CXF的java.io.FileNotFoundException: class path resource [META-INF/cxf/cxf-extension-soap.xml] cannot be opened because it does not exist
以下是错误信息 九月 25, 2017 8:22:04 下午 org.springframework.web.context.support.XmlWebApplicationContext prep ...
- appium+Python 启动app(一)
当我们appium和Python环境都配置好了,如何启动我们第一个app呢?下面介绍appium+Python启动app的操作步骤,为了能够详细查看,我们这里使用夜游神模拟器进行示范. 测试项目:QQ ...
- Java经典编程题50道之十
一球从100米高度自由落下,每次落地后反跳回原高度的一半:再落下……求它在第10次落地时,共经过多少米?第10次反弹多高? public class Example10 { public sta ...
- InputStream中通过mark和reset方法重复利用缓存
通过缓存InputStream可重复利用一个InputStream,但是要缓存一整个InputStream内存压力可能是比较大的.如果第一次读取InputStream是用来判断文件流类型,文件编码等用 ...
- vue——安装并新建项目
一.对于vue的安装: 1.安装vue之前先安装node,https://nodejs.org/zh-cn/download/,我装的是windows64位的: 2.下载好了之后就可以按照正常顺序安装 ...