一、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. Js中的apply和call

    1.call和apply都是为了改变某个函数运行时的上下文而存在的 2.也就是改变函数体内this的指向. 3.二者的作用完全一样,只是接受参数的方式不太一样. 4.call 需要把参数按顺序传递进去 ...

  2. eclipse安装Activiti Designer插件(转载:http://blog.csdn.net/qq_33547950/article/details/54926435)

    为了完成毕业设计,需要学习Activiti.万事开头难,果然刚开始就遇到了问题.<Activiti实战>和视频教程里提供的安装Activiti Designer插件方法(即下文方法一)不能 ...

  3. python 的for else语句

    for中间不是break出来的,是正常循环完跳出循环的会执行else内的语句 while else语句也是如此 这个以前的常见语言没有,特此记录

  4. Flex自定义组件开发

    一般情况下需要组件重写都是由于以下2个原因:1.在FLEX已有组件无法满足业务需求,或是需要更改其可视化外观等特性时,直接进行继承扩展.2.为了模块化设计或进一步重用,需要对FLEX组件进行组合.而F ...

  5. UVA - 10870 Recurrences 【矩阵快速幂】

    题目链接 https://odzkskevi.qnssl.com/d474b5dd1cebae1d617e6c48f5aca598?v=1524578553 题意 给出一个表达式 算法 f(n) 思路 ...

  6. mysql设置有外键的主键自增及其他

    有外键的主键设置自增. ; ALTER TABLE `<table>` MODIFY COLUMN `id` ) NOT NULL AUTO_INCREMENT FIRST; 创建数据库, ...

  7. ubuntu14.04 安装pip vitualenv flask

    安装pip: $ apt-get install python-pip$ pip -V #查看版本 确认安装成功 安装完pip后,会发现下载的速度特别慢.按如下修改: $ vim ~/.pip/pip ...

  8. [原创]java WEB学习笔记04:Servlet 简介及第一个Servlet程序(配置注册servlet,生命周期)

    本博客为原创:综合 尚硅谷(http://www.atguigu.com)的系统教程(深表感谢)和 网络上的现有资源(博客,文档,图书等),资源的出处我会标明 本博客的目的:①总结自己的学习过程,相当 ...

  9. EntityFramework 学习 一 Migration from Entity Framework 4.1/4.3 to Entity Framework 5.0/6.0

    To migrate your existing Entity Framework 4.x project to Entity Framework 5.0 using VS2012, first ta ...

  10. Spark集群搭建(local、standalone、yarn)

    Spark集群搭建 local本地模式 下载安装包解压即可使用,测试(2.2版本)./bin/spark-submit --class org.apache.spark.examples.SparkP ...