[ACM_动态规划] 轮廓线动态规划——铺放骨牌(状态压缩1)
Description
Squares and rectangles fascinated the famous Dutch painter Piet Mondriaan. One night, after producing the drawings in his 'toilet series' (where he had to use his toilet paper to draw on, for all of his paper was filled with squares and rectangles), he dreamt of filling a large rectangle with small rectangles of width 2 and height 1 in varying ways.
Expert as he was in this material, he saw at a glance that he'll need a computer to calculate the number of ways to fill the large rectangle whose dimensions were integer values, as well. Help him, so that his dream won't turn into a nightmare!
Input Specification
The input file contains several test cases. Each test case is made up of two integer numbers: the height h and the width w of the large rectangle. Input is terminated by h=w=0. Otherwise, 1<=h,w<=11.
Output Specification
For each test case, output the number of different ways the given rectangle can be filled with small rectangles of size 2 times 1. Assume the given large rectangle is oriented, i.e. count symmetrical tilings multiple times.
Sample Input 1 2 |
Sample Output 1 |
Input
The input file contains several test cases. Each test case is made up of two integer numbers: the height h and the width w of the large rectangle. Input is terminated by h=w=0. Otherwise, 1<=h,w<=11.
Output
For each test case, output the number of different ways the given rectangle can be filled with small rectangles of size 2 times 1. Assume the given large rectangle is oriented, i.e. count symmetrical tilings multiple times.
Sample Input
1 2
1 3
1 4
2 2
2 3
2 4
2 11
4 11
0 0
Sample Output
1
0
1
2
3
5
144
51205
今天集训做这题用怎样的dp都会跪,然后我就自己模拟,还是跪......最后前辈SD指导,原来这是典型的状态压缩问题(所谓状态压缩也弄不懂啥专业术语,就知道针对这题采用的是二进制策略将d[2][][][][][]...后面的m维数组映射为一个2进制的m位,从而用一个d[2][1<<m]的二维数组就搞定啦),下面是对这题我的理解....我是从一点不懂,然后从多段图路径看到轮廓线动态压缩,然后又根据图研究本题的构造方法,最后又理解2进制操作,感觉做ACM的题目好充实,哈哈,加油!!!
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m,cur; const int maxn=;
long long d[][<<maxn]; int main(){
while(scanf("%d%d",&n,&m)==){
if(m== && n==)return ;
if(n<m)swap(n,m);
memset(d,,sizeof(d));
cur=;
d[][(<<m)-]=;//所有d[0][j]初始化为1
for(int i=;i<n;i++)
for(int j=;j<m;j++){//从小到大枚举每一个阶段(共m*n阶段)
cur^=;
memset(d[cur],,sizeof(d[cur]));
for(int k=;k<(<<m);k++){//枚举上个阶段的状态(0-2*m-1)
if(k&(<<m-))d[cur][(k<<)^(<<m)]+=d[-cur][k];//不放
if(i && !(k&(<<m-)))d[cur][(k<<)^]+=d[-cur][k];//插上
if(j && (k&(<<m-)) && !(k&))d[cur][(k<<)^(<<m)|(<<)^]+=d[-cur][k];//插左
}
}
printf("%lld\n",d[cur][(<<m)-]);
}
return ;
}
/*《明明很爱你——品冠、梁静茹,伤感唯美》《红日——杨克勤,热血,积极》《如果你也听说——张惠妹,爱情,伤感》
^_^!状态压缩——轮廓线动态规划
^_^!共同特点:在一个比较"窄"(行数少或者列数少)的棋盘上进行复杂操作。如过采用传统方法(以整行或者整列为状态)进行规划,
将无法进行状态转移,因此只能把参差不齐的轮廓线也作为状态转移的一部分。
^_^!方法:要用到递推关系里的多段图路径问题:从左到右有n列节点,每列称为"一个阶段",每个阶段的结点只能向下一阶段连有向
边(每个结点出发可以连多条边),求从阶段1到阶段n每个结点的路径条数(起点可以任意,只要是从阶段1开始的即可)
>> 设d[i][j]为从阶段1到结点(i,j)的路径条数,则伪代码为(已用滚动数组)
>> cur=0;
>> 所有d[0][j]初始化为1
>> 从小到大枚举每个要算的阶段{ //采用动态数组思想实现上下2层数据更新
>> cur^=1;//d[2][m]--->一层为d[cur][...]另一层为d[1-cur][...]两层数据轮换为cur^=1;
>> 所有d[cur][j]初始化为0//只能放在这,因为在d[cur]存着以前某个阶段的值
>> for 上个阶段每个结点j
>> for j的每个后继结点k
>> d[cur][k]+=d[1-cur][j]
>> }
>> 这里cur就是"正在计算"的那一阶段。最后d[cur]为阶段n各结点的值。不难发现,这些节点的名字和编号并不重
>> 要,只需要从小到大枚举这些阶段就好
^_^!本题:铺放骨牌(用1x2的骨牌覆盖nxm的棋盘,有多少种方法?输入韩多组数据,每组包含2个整数m,n(m*n<=100),当m==n==0时
结束),对于每组数据输出总数。
^_^!解题思路:1>因为题目中n*m<=100所以m、n至少有一个不超过10。为简单起见,我们规定m=<n,如果n>m就交换,来符合前面说的
"窄"棋盘条件;
2>这里按照从上到下、从左到右的顺序把棋盘分成m*n个阶段,每个阶段包含m个棋盘区域(第n行m列的1x1的方块及其后
的m-1个),我们用1已覆盖,0表示未被覆盖,这样每个阶段所有情况就是所有m位二进制,共2的m次方个结点;
3>阶段决策是:"以当前格子为右下角,要不要放骨牌以及放哪种骨牌"。答案有3种:不放骨牌、放竖骨牌、放横骨牌
$(i,j)在右下角竖放即插入上一层,横放是占据前一个和(i,j)位置$ 【】【】【】【】【】
【】【】【】【】【】
【】【】[k4][k3][k2]
[k1][k0](i,j)
|
|
|----------- 【】【】【】【】【】
| 【】【】【】【】【】
| 【】【】 1 [k3][k2]
| [k1][k0][0] //不放的情况只有当k4==1时,转移到k3k2k1k00状态
|
|----------- 【】【】【】【】【】
| 【】【】【】【】【】
| 【】【】 1 [k3][k2]
| [k1][k0][1] //往上放的情况只有当k4==0 && i!=最上层时,转移到k3k2k1k01
|
|----------- 【】【】【】【】【】
| 【】【】【】【】【】
| 【】【】 1 [k3][k2]
| [k1][1] [1] //往左放的情况只有当k4==1 && k0==0 && j不是最左层时,转移到k3k2k111
|
^_^!算法复杂度为O(mn*2^m),最终答案为d[cur][2^m-1] (因为是从0开始编号的最后一个阶段即m*n-1,又因为该算法采用滚动
数组法,所以最后一个阶段数值保存在d[cur][...]里,上面说的设d[i][j]为从阶段1到结点(i,j)的路径条数,这里我们为了
防止开一个d[2][k4][k3][k2][k1][k0]的数组,所以将后面m维映射为一个2进制整数2^k4+2^k3+2^k2+2^k1+2^k0共
是0-2^m-1个结点,所以最后一个阶段最后一个结点存的就是答案)
^_^!位运算实现状态转移:设旧状态为k,则:
A:不放,条件:二进制k从左往右第一个为1,即:k & (1<<m-1));
新状态为k左移一位的结果:k<<1;
B:上插,条件:二进制k从左往右第一个为0 && i不为顶,即:i && !(k&(1<<m-1))
新状态为k左移一位最后一位变为1:(k<<1)^1
C:左插,条件:二进制k最左一位为1,最右一位为0,且j不为左,即:j && (k&(1<<m-1)) && !(k&1)
新状态为k左移一位最后一位和倒数第二位变为1
*/
[ACM_动态规划] 轮廓线动态规划——铺放骨牌(状态压缩1)的更多相关文章
- 动态规划——用二进制表示集合的状态压缩DP
动态规划当中有非常常见的一个分支--状态压缩动态规划,很多人对于状态压缩畏惧如虎,但其实并没有那么难,希望这文章能带你们学到这个经典的应用. 二进制表示状态 在讲解多重背包问题的时候,我们曾经讲过二进 ...
- UVa 11270 铺放骨牌(轮廓线DP)
https://vjudge.net/problem/UVA-11270 题意: 用1×2骨牌覆盖n×m棋牌,有多少种方法? 思路: 这道题目是典型的轮廓线DP题. 所谓轮廓线DP,就是以整行整列为状 ...
- 铺放骨牌 uva11270
题解: 插头dp裸题 没什么好说的啊就是n个二进制位表示状态 相比原先就是用2n个二进制位表示状态 蓝书上后面几题插头dp都挺烦的啊... 代码:
- 转 状态压缩DP
引入 首先来说说“状态压缩动态规划”这个名称,顾名思义,状态压缩动态规划这个算法包括两个特点,第一是“状态压缩”,第二是“动态规划”. 状态压缩: 从状态压缩的特点来看,这个算法适用的题目符合以下的条 ...
- vijos1426兴奋剂检查(多维费用的背包问题+状态压缩+hash)
背景 北京奥运会开幕了,这是中国人的骄傲和自豪,中国健儿在运动场上已经创造了一个又一个辉煌,super pig也不例外……………… 描述 虽然兴奋剂是奥运会及其他重要比赛的禁药,是禁止服用的.但是运动 ...
- 状态压缩动态规划 状压DP
总述 状态压缩动态规划,就是我们俗称的状压DP,是利用计算机二进制的性质来描述状态的一种DP方式 很多棋盘问题都运用到了状压,同时,状压也很经常和BFS及DP连用,例题里会给出介绍 有了状态,DP就比 ...
- UVA11270 Tiling Dominoes(轮廓线动态规划)
轮廓线动态规划是一种基于状态压缩解决和连通性相关的问题的动态规划方法 这道题是轮廓线动态规划的模板 讲解可以看lrj的蓝书 代码 #include <cstdio> #include &l ...
- 【arc093f】Dark Horse(容斥原理,动态规划,状态压缩)
[arc093f]Dark Horse(容斥原理,动态规划,状态压缩) 题面 atcoder 有 \(2^n\) 名选手,编号为 \(1\) 至 \(2^n\) .现在这 \(2^n\) 名选手将进行 ...
- ZJOI 2009 多米诺骨牌(状态压缩+轮廓线+容斥)
题意 https://www.lydsy.com/JudgeOnline/problem.php?id=1435 思路 一道很好的状压/容斥题,涵盖了很多比较重要的知识点. 我们称每两行间均有纵跨.每 ...
随机推荐
- UART UVM验证平台平台搭建总结
tb_top是整个UVM验证平台的最顶层:tb_top中例化dut,提供时钟和复位信号,定义接口以及设置driver和monitor的virual interface,在intial中调用run_te ...
- 实现MFC菜单画笔画圆,并且打钩
这个是用最简单的方法,移动客户区,圆会不见,所以下一篇我还要改进. 首先新建一个MFC单文件,在资源那里的菜单下,建立画笔,可以弹出红画笔,蓝画笔和绿画笔,,给出ID_RED,ID_BLUE,ID_G ...
- php 通过变量 来调用函数
<?php function fun() { echo 'fun'; } $a = 'fun'; $a(); ?> 复制代码 上面的$a变量就是fun()函数,调用$a()和调用fun() ...
- JS打印页面
打印 整个html页面(PS:样式要写在页面里面才能打印(就是用内部样式)) <a id="dayi" runat="server ...
- 在WINDOWS上安装oracle database 11
1:在CD-ROM中插入oracle database 11G安装盘会自动运行程序,打开[欢迎使用]窗口 2:弹出[选择安装类型] 3:弹出[制定主目录详细信息]‘oracle基目录’:用于设置环境变 ...
- 大道至简之编程的精义读后感(Java伪代码)
import.java.大道至简.*; import.java.愚公移山.*; public class YuGongYiShan { 愚公={项目组织者,团队经理,编程人员,技术分析师}: //沟通 ...
- 为什么for(int i=0;i<9;i++) 在c语言中是错误的?
显示表示,i 变量不可以在for中定义,必须在外面定义,这是为什么? 因为C99标准以前的C标准是不支持临时变量在for循环中定义的. C99标准就支持这样写.但是目前有些编译器并不怎么愿意支持C99 ...
- 洛谷P2014 选课 (树形dp)
10月1日更新.题目:在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习,在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习.现在有N门功课,每门课有个学分 ...
- Hibernate缓存之初探
数据层的访问效率优化可能第一想到的就是利用缓存,缓存的机能可以简单理解为将从数据库中访问的数据放在内存中,在以后再次使用到这些数据时可以直接从内存中读取而不必要再次访问数据库,尽量减少和数据库的交互提 ...
- Centos6.X下安装php nginx mysql 环境
---------------------------------------更换163软件源,此步可以省略,记得把repo文件里面的6.5改成当前版本号 yum makecache &&am ...