一、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

The input 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.

二、题解

        目标:计算用宽为2个单位,高为1个单位的小矩形,可以有多少种方式填充给定宽和高的大矩形。

        方法:压缩矩阵DP

        这是小弟第一次接触到压缩矩阵DP,前天看了一下,没有什么头绪。今天咬着牙把达 文西 写的 状态压缩动态规划 POJ 2411 (编程之美-瓷砖覆盖地板) 仔细研读了一遍,把程序了推敲了一下。总算明白了一点,特此记下。

        对于状态压缩DP,其实最简单的理解就是把状态用比特位的形式表示出来。宽度就表示比特的位数,大家知道每位上有0和1两种形式。多个比特位的组合就能表示多个状态,而状态最多是2^width个。所以,这也意味着每一行有2^width种形式。但是,不是每一种形式都满足填充的要求。首先介绍一下这种用0和1表示小矩形的方式。在位置(i, j) 如果我们选择横着贴砖,那么将(i, j), (i, j+1)都填写成1, 如果竖着贴砖,我们将(i,j)填写成0, 将(i+1, j)填写成1.

        刚才说到,每一行有多种表示,但不是每一种都符合要求,我们把符合要求的成为状态兼容,比如,宽度为2时,0-1的行组合是不符合要求的,因为单独的一个1是不符合要求的(在上一行没有0的情况下),这时就是状态不兼容。关键是我们怎么把这种表现的形式用到矩阵中。这里多次用到了移位操作,向左每移移位相当于乘2,这里采用了16进制的表示形式(如Ox1,表示16进制的1)。对于不兼容的情况有多种,如下所述:

假如现在我们在铺砖 位置(i, j), 并且假设之前的位置已经铺设好的了,在这个位置,我们的选择:

1. 不用铺砖了,可能在(i-1, j)的时刻已经被竖着铺上了,然后考虑的是(i, j+1)

2. 横铺砖,将(i, j+1)也铺上了,然后考虑的是(i, j+2)

3. 竖着铺砖,(将i,j)和(i+1,j)铺上一个竖立的转头。

所以我们如下翻译我们的选择,在位置(i, j) 如果我们选择横着贴砖,那么将(i, j), (i, j+1)都填写成1, 如果竖着贴砖,我们将(i,j)填写成0, 将(i+1, j)填写成1.

为什么要这么计数呢,我觉得应该这样理解:

1. 在横着贴砖的时候,(i, j), (i, j+1) 都是1,这个值其实对下一行如何选择没有影响。

2. 竖着贴砖的第二个,我们也选择了1, 因为这个砖头结束了,对下一行如何选择依然没有影响。

3. 而竖着的第一个砖头,这个砖头是对下面有影响的,如果(i,j)是0,那么(i+1, j)只有是1的情况下才能满足条件。

(这涉及到接下来的 状态兼容性问题)

对于竖着贴砖为什么这样选择,这样选择的一个好处是,我们在处理最后一行的时候,可以保证最后一行都是1, 因为最后一行绝对不能成为 竖砖开始,所以很容易取得最后的解。

好了,我们把这样理解的方案画成图:

如果我们将每一行都理解成一个二进制数字,那么

Row1 = 51,  Row2 = 15, Row3 = 48, Row4 = 63, Row5 = 51, Row6 = 63.

最后转头铺满的状态,一定是最后一行全是1。

我们用DP(i,j) 表示如下含义: 当第i行,达到状态j的时候,所能采取的方案数目。 所以明显我们的最后目的是求 DP(N, 2^(M-1)-1);

我们再来简单的分析一下为什么问题可以满足动态规划, 加入现在分析的对象是 DP(i,j), 那么这一行有多少种铺设办法是和上一行相关的,

如果上一行的某个状态DP(i-1,k) 可以达到 DP(i, j) 我们认为这两个状态是兼容的,如果DP(i-1,k)和DP(i, j)兼容并且 DP(i-1, k)有S中铺设方案,那么DP(i, j)就可以从DP(i-1, k)

这条路径中获得S个方案。 当然这里k的取值可以是 0 ~~~~ 2^(M-1) -1种取值。

现在我们来理解一下,什么叫做 j, k 兼容。

其实我们在上面已经基本给出分析, 如果我们现在铺设 (i,x) x这里表示第i行,第x列

1. 如果值 i  行,j 在x位上的值是0, 那么第 i-1行,j的值在x位上一定是1。因为不可能在同一列相邻的位置铺两个竖着的 第一个,如果满足下一步测试的是(i, x+1), 否则直接返回不兼容。

2. 如果值 i  行,j在x位置的值是1 .

{

那么有可能有两种情况:

1. (i-1, x)是0, 这个时候一定是竖着铺设了,下一步检测的是(i, x + 1)

2.  (i-1, x) 是1, 如果是这样的话,那么(i, x)一定是要选择横着铺了,那么(i,x+1)也一定是1,并且(i-1, x + 1)一定是1(如果是0,就是竖着铺了),如果不满足就返回不兼容,满足条件 就测试(i, x + 2)

}

对于第一行的兼容性,由于没有上一行的影响,我们要做一下特别的分析。

加入当前测试的是 DP(0, j)的第 x的比特位,即第0行,x列

1. 如果x是1,那么 x + 1 也一定是1,然后测试到 x + 2

2. 如果x是0, 那么直接测试下一个 x + 1

刚才说到怎么把这种思想用什么方式实现是最关键的,这里巧妙用到了移位和位的与(&)来比较判定每个兼容性。压缩矩阵DP是第一次见,有些东西看了理解的不是很全面,以后还要改正。

三、java代码

import java.util.Arrays;
import java.util.Scanner; public class Main{ static int MAX_ROW =11;
static int MAX_STATUS= 2048; // 2^(12-1)=2048;
static long[][] DP=new long[MAX_ROW][MAX_STATUS];
static int g_Width;
static int g_Height; static boolean TestFirstLine(int nStatus){ //test the first line
int i = 0;
while(i < g_Width){
if((nStatus & (0x1 << i))!=0){
//i == g_Width -1表示1—0的情况,(nStatus & (0x1 << (i+1))) == 0)表示0-1的情况
if( i == g_Width -1 || (nStatus & (0x1 << (i+1))) == 0){
return false;
}
i += 2;
}
else{
i++;
}
}
return true;
} static boolean CompatibilityTest(int nStatusA, int nStatusB){ // test if status (i, nStatusA) and (i-1, nStatusB) is compatible.
int i = 0;
while( i < g_Width){
if((nStatusA & (0x1 << i)) == 0){ //两行的同一列中都为0的情况,不兼容
if((nStatusB & (0x1 << i)) == 0){
return false;
}
i++; //两行的同一列中为0-1的情况,兼容,继续下一个位置
}
else{
if((nStatusB & (0x1 << i)) == 0 ){ //两行的同一列中为1-0的情况,兼容,继续下一个位置
i++;
}
//
else if( (i == g_Width - 1) || !(((nStatusA & (0x1 << (i+1)))!=0) &&
(nStatusB & (0x1 << (i + 1)))!=0) ){ return false;
}
else{
i += 2;
} }
}
return true;
}
public static void main(String[] args){
Scanner cin = new Scanner(System.in);
int i,j;
int k;
while(true){
g_Height=cin.nextInt();
g_Width=cin.nextInt(); if(g_Width == 0 && g_Height == 0){
break;
} if(g_Width > g_Height){
int temp=g_Height;
g_Height=g_Width;
g_Width=temp;
}
int nAllStatus = 2 << (g_Width-1); //所有的状态位,相当于2^g_width for(i=0;i< DP.length;i++)
Arrays.fill(DP[i],0); for( j = 0; j < nAllStatus; j++){
if(TestFirstLine(j)){
DP[0][j] = 1;
}
}
for( i = 1; i < g_Height; i++){
for( j = 0; j < nAllStatus; j++){ // iterate all status for line i
for( k = 0; k < nAllStatus; k++){ // iterate all status for line i-1
if(CompatibilityTest(j, k)){
DP[i][j] += DP[i-1][k];
}
}
}
}
System.out.println( DP[g_Height-1][nAllStatus - 1]);
}
}
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

Poj 2411 Mondriaan's Dream(压缩矩阵DP)的更多相关文章

  1. POJ 2411 Mondriaan's Dream -- 状压DP

    题目:Mondriaan's Dream 链接:http://poj.org/problem?id=2411 题意:用 1*2 的瓷砖去填 n*m 的地板,问有多少种填法. 思路: 很久很久以前便做过 ...

  2. POJ - 2411 Mondriaan's Dream(轮廓线dp)

    Mondriaan's Dream Squares and rectangles fascinated the famous Dutch painter Piet Mondriaan. One nig ...

  3. Poj 2411 Mondriaan's Dream(状压DP)

    Mondriaan's Dream Time Limit: 3000MS Memory Limit: 65536K Description Squares and rectangles fascina ...

  4. POJ 2411 Mondriaan's Dream ——状压DP 插头DP

    [题目分析] 用1*2的牌铺满n*m的格子. 刚开始用到动规想写一个n*m*2^m,写了半天才知道会有重复的情况. So Sad. 然后想到数据范围这么小,爆搜好了.于是把每一种状态对应的转移都搜了出 ...

  5. poj 2411 Mondriaan's Dream (轮廓线DP)

    题意:有一个n*m的棋盘,要求用1*2的骨牌来覆盖满它,有多少种方案?(n<12,m<12) 思路: 由于n和m都比较小,可以用轮廓线,就是维护最后边所需要的几个状态,然后进行DP.这里需 ...

  6. POJ 2411 Mondriaan's Dream 插头dp

    题目链接: http://poj.org/problem?id=2411 Mondriaan's Dream Time Limit: 3000MSMemory Limit: 65536K 问题描述 S ...

  7. [poj 2411]Mondriaan's Dream (状压dp)

    Mondriaan's Dream Time Limit: 3000MS Memory Limit: 65536K Total Submissions: 18903 Accepted: 10779 D ...

  8. [POJ] 2411 Mondriaan's Dream

    Mondriaan's Dream Time Limit: 3000MS Memory Limit: 65536K Total Submissions: 18903 Accepted: 10779 D ...

  9. poj 2411 Mondriaan's Dream(状态压缩dP)

    题目:http://poj.org/problem?id=2411 Input The input contains several test cases. Each test case is mad ...

随机推荐

  1. ORDER BY today_used ASC' % (MAX_USED_TIMES)

    python D:\pymine\clean\spider_map\get_bd_uid_rest_b.py python D:\pymine\clean\spider_map\get_bd_uid_ ...

  2. A norm is a function. 范数是函数。

    [范数]范数是函数.A norm is a function.范数(norm),是具有“长度”概念的函数.在线性代数.泛函分析及相关的数学领域,是一个函数,其为向量空间内的所有向量赋予非零的正长度或大 ...

  3. php递归删除所有文件

    function del_file($dir) { if (@rmdir($dir)==false && is_dir($dir)) { if ($dp = opendir($dir) ...

  4. OC中第三方库MJExtension的使用

    MJExtension是一套常用的"字典和模型之间互相转换"的框架,在项目中也使用过,现在记录一下.随着Swift的普及,在Swift中也有一个类似功能的框架HandyJSON 也 ...

  5. CALL TRANSFORMATION 的方法生成XML文件

    *&---------------------------------------------------------------------**& Report  Z_BARRY_X ...

  6. eclipse InvocationTargetException 错误解决

    1.今天做一个推送的,用到了几个jar包,直接以User Library的形式加进 在单元测试中,测试的很好,没有任何问题, 但是在action中测试,一测试就崩. 跟踪以后出现InvocationT ...

  7. php中生成随机密码的自定义函数代码

    这篇文章主要分享下php中生成随机密码的方法,原理就是把一些要生成的字符预置一个的字符串包括数字拼音之类的以及一些特殊字符,这样我们再随机取字符组成我们想要的随机密码了 代码一: 生成一个随机密码的函 ...

  8. selenium鼠标悬停操作

    有些网页一打开会有一个弹窗,弹窗不消失无法进行取元素操作,只有把鼠标悬停在上面弹窗才会消失,这时就用到了selenium的悬停操作 鼠标悬停  move_to_element() 定位到要悬停的元素 ...

  9. fields_for

    1 一对多 Using Strong Parameters With Fields For & Nested Forms in Rails 4 http://www.rubyexperimen ...

  10. 用cocos2d-html5做的消除类游戏《英雄爱消除》(2)——Block设计实现

    Block可以说是这个游戏的核心类,它除了包含自身的一些属性和方法外还添加了对触摸事件的响应. 我们先来看下源码吧 /** * Power by html5中文网(html5china.com) * ...