多米诺骨牌放置问题(状压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\)我们有一个操作是可以对调上下的点数.若记一块 ...
随机推荐
- Java经典编程题50道之四十四
求0~7所能组成的奇数个数.分析:组成1位数是4个,组成2位数是7*4个,组成3位数是7*8*4个,组成4位数是7*8*8*4个…… public class Example44 { publi ...
- 轻松搭建Git服务器(Ubuntu)
搭建Git服务器 在远程仓库节中,我们讲了远程仓库实际上和本地仓库没啥不同,纯粹为了7x24小时开机并交换大家的修改. GitHub就是一个免费托管开源代码的远程仓库.但是对于某些视源代码如生命的商业 ...
- Spring data mongodb ObjectId ,根据id日期条件查询,省略@CreatedDate注解
先看看ObjectId 的json 结构,非常丰富,这里有唯一机器码,日期,时间戳等等,所以强烈建议ID 使用 ObjectId 类型,并且自带索引 Spring data mongodb 注解 @C ...
- 基础--Redis在Linux环境下的安装
1. 安装redis服务 1.1 检查安装依赖程序 yum install gcc-c++yum install -y tclyum install wget 1.1.1 下载redis安装包 (或者 ...
- SpringBoot项目在IntelliJ IDEA中实现热部署
spring-boot-devtools是一个为开发者服务的一个模块,其中最重要的功能就是自动应用代码更改到最新的App上面去.原理是在发现代码有更改之后,重新启动应用,但是速度比手动停止后再启动更快 ...
- C语言老司机学Python (六)- 多线程
前面的1-5都是比较基础的东西,能做的事情也有限. 从本节起,随着更多进阶技术的掌握,渐渐就可以用Python开始浪了. Python3使用threading模块来实现线程操作. 根据在其他语言处学来 ...
- Hive导入HDFS/本地数据
#创建表人信息表 person(String name,int age) hive> create table person(name STRING,age INT)ROW FORMAT DE ...
- eclipse hadoop1.2.0配置及wordcount运行
"error: failure to login"问题 http://www.cnblogs.com/xia520pi/archive/2012/05/20/2510723.htm ...
- openstack-ocata-环境准备1
Openstack环境准备1. 最少两台机器2. Controller:1核cpu 4G内存 5G硬盘3. Computer:1核cpu 2G内存 10G硬盘4. 至少两个网卡,本次采用四个网卡(1网 ...
- The required anti-forgery form field "__RequestVerificationToken" is not present.
@using (Html.BeginForm()) { @Html.AntiForgeryToken() }