题意:略。

思路:这一题开始做的时候完全没有思路,便去看了别人的题解。

首先,对于这个题目解法想有一个初步的了解,请看这里:http://www.2cto.com/kf/201208/146894.html

根据这篇讲解,写了一篇扭曲的代码,提交之后TLE。

经过排查分析之后发现,算法的复杂度为O(hw*(2^(2w))),这个复杂度肯定超了。后来进行了优化,如果两种状态可以匹配,就将它们用邻接表(vector实现)存储起来,这样只需一遍预处理,以后直接读取就可以了。

此外,还有两个地方的优化:

1. 如果h*w为奇数,则结果必为0。(每个砖块的面积为2,无法用整数块铺满)

2. 如果h < w, 将两者数值交换。

后面还有一种dfs+dp的解法,我觉得很精巧,在下面重点分析。这里先贴下上面方法的代码。

 #include<stdio.h>
#include<iostream>
#include<vector>
#include<string.h>
#include<algorithm>
using namespace std;
long long dp[<<][];
vector<int> ok[<<];
int h, w;
bool judge(int up, int down)
{
int u[], d[];
int now = w;
while (now)
{
u[now] = up % ;
d[now--] = down % ;
up /= ;
down /= ;
}
for (int i = ; i <= w;)
{
if (!d[i])//该行该位为0
{
if (!u[i]) return ;//上一行若也为0,则不合法
i++;
}
else if (!u[i])//该行该位为1,且上一行该位为0
i++;
else//该行该位为1,且上一行该位也为1
{
if (i + > w || !d[i+] || !u[i+]) return ;
i += ;
}
}
return ;
}
long long getdp()
{
memset(dp, , sizeof(dp));
for (int i = ; i < (<<w); i++)
{
ok[i].clear();
for (int j = ; j < (<<w); j++) if (judge(j, i))
ok[i].push_back(j);
}
for (int i = ; i < ( << w); i++)
for (int j = ; j < ok[i].size(); j++) if (ok[i][j] == (<<w) - )
dp[i][] = ;
for (int i = ; i <= h; i++)
for (int j = ; j < ( << w); j++)
for (int k = ; k < ok[j].size(); k++)
dp[j][i] += dp[ ok[j][k] ][i-];
return dp[(<<w)-][h];
}
int main()
{
while (~scanf("%d%d", &h, &w) && h && w)
{
if ((h * w) % )
{
printf("0\n");
continue;
}
if (h < w) swap(h, w);
printf("%lld\n", getdp());
}
return ;
}

===========分割线===============

dfs的方法:

状态压缩的原则与上一种方法是一样的:如果该位为0,则说明该处为一竖放的砖块,且为该砖块的上半部分;其余为1。

核心部分是dfs的方法。首先,dp[state][row]表示铺到第row行,且该行状态为state时的方法总数,这并没有变。每次枚举状态,与上面不同的是,枚举的是上一层的状态。

上一种方法:

for(i = 2; i <= h; i++)//枚举行数

  for(j = 0; j < (1<<w); j++)//枚举该行的状态

    for(k...)//枚举该行可匹配的上一行状态

dfs版:

for(i = 2; i <= h; i++)//枚举行数

  for(j = 0; j < (1<<w); j++)//枚举上一行的状态

    if(...) dfs(...)//如果上一行该状态方法数不为0,则dfs遍历该行可行状态

遍历的方法很巧妙:假设所枚举到的上一行状态为s,则将s每一位都取反便是该行的一种可行状态。因为,如果s的某一位是0,说明这是一竖放砖块的上半部分,取反后恰好就是下半部分对应的1;如果s某一位是1,取反后是0,这当然也是可行的。

又由于当s某一位是1时,下一行对应的位可以是0或者1(若是1则必然是横放的,需要连续两位状态都是1)。所以在dfs的过程中需要遍历所有两个0相邻的情况,将他们置为1。当dfs的下标pos到达最后一位(即pos=w)时,说明该状态是可行的,就将该状态的dp值加上上一行状态s的dp值。因此,每次dfs之前都需要暂时存储下上一行状态s的dp值。

这种方法代码既短又巧妙,让人佩服啊。

 #include<stdio.h>
#include<algorithm>
#include<string.h>
using namespace std;
long long dp[<<][], tem, h, w;
void dfs(int row,int state,int pos)
{
if (pos == w)
{
dp[state][row] += tem;
return;
}
dfs(row, state, pos + );
if (pos <= w - && !(state & (<<pos)) && !(state & (<<(pos + ))))
dfs(row, state | <<pos | <<(pos+), pos + );
}
int main()
{
while (~scanf("%d%d", &h, &w) && h && w)
{
if (h * w % )
{
printf("0\n");
continue;
}
if (h < w) swap(h, w);
memset(dp, , sizeof(dp));
tem = ;
dfs(, , );
for (int i = ; i <= h; i++)
for (int j = ; j < (<<w); j++) if (dp[j][i-])
{
tem = dp[j][i-];
dfs(i, ~j & ((<<w) - ), );
}
printf("%lld\n", dp[(<<w)-][h]);
}
return ;
}

POJ 2411 Mondriaan's Dream [经典状态压缩dp]的更多相关文章

  1. poj 2411 Mondriaan's Dream(状态压缩dp)

    Description Squares and rectangles fascinated the famous Dutch painter Piet Mondriaan. One night, af ...

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

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

  3. POJ 2411 Mondriaan's Dream (状压DP,骨牌覆盖,经典)

    题意: 用一个2*1的骨牌来覆盖一个n*m的矩形,问有多少种方案?(1<=n,m<=11) 思路: 很经典的题目,如果n和m都是奇数,那么答案为0.同uva11270这道题. 只需要m个b ...

  4. POJ 2411 Mondriaan's Dream/[二进制状压DP]

    题目链接[http://poj.org/problem?id=2411] 题意:给出一个h*w的矩形1<=h,w<=11.用1*2和2*1的小矩形去填满这个h*w的矩形,问有多少种方法? ...

  5. POJ 2411 Mondriaan's Dream 【状压Dp】 By cellur925

    题目传送门 这道题暑假做的时候太模糊了,以前的那篇题解大家就别看了==.今天再复习状压感觉自己当时在写些什么鸭.... 题目大意:给你一个\(n\)*\(m\)的棋盘和许多\(1*2\)的骨牌,骨牌可 ...

  6. Poj 2411 Mondriaan's Dream(压缩矩阵DP)

    一.Description Squares and rectangles fascinated the famous Dutch painter Piet Mondriaan. One night, ...

  7. POJ 2411 Mondriaan's Dream 插头dp

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

  8. POJ 2411 Mondriaan's Dream

    思路:状态压缩dp,如果在(i,j)位置横着放砖块,那么(i,j)和(i+1.j)都是1,如果竖着放砖块,那么(i,j)为0,(i,j+1)为1,这样每行就可以用一个整数来存放状态,设dp[i][j] ...

  9. POJ 2411.Mondriaan's Dream 解题报告

    题意: 给出n*m (1≤n.m≤11)的方格棋盘,用1*2的长方形骨牌不重叠地覆盖这个棋盘,求覆盖满的方案数. Solution:                位运算+状态压缩+dp       ...

随机推荐

  1. Redis实现之字符串

    简单动态字符串 Redis中的字符串并不是传统的C语言字符串(即字符数组,以下简称C字符串),而是自己构建了一种简单动态字符串(simple dynamic string,SDS),并将SDS作为Re ...

  2. SpringMvc路径参数和url的两种实现方式

    我们经常采用的SpringMvc路径参数经常的操作是在url后面采用?参数名=值1&参数名2=值2这种方式实现 RequestMapping的作用: 1)当作用在controller时,我们通 ...

  3. datagrid的右键菜单

    1. 2.右键菜单,主要是用onRowContextMenu:function(e,index,row){}方法来实现 onRowContextMenu:function(e,index,row){ ...

  4. easyui的layout

    1.浏览器自适应(即浏览器改变大小,里面的表格大小也会随之改变)要设置两个参数 (1)一般都要在body上设置class=“easyui-layout”: <body class="e ...

  5. JSP自定义tag控件标签

    JSP支持自定tag的方法,那就是直接讲JSP代码保存成*.tag或者*.tagx的标签定义文件.tag和tagx文件不仅支持经典jsp代码,各种标签模版代码,还支持xml样式的jsp指令代码. 按照 ...

  6. CSS滚动插件

    http://www.dowebok.com/131.html  wow.js http://www.jq22.com/jquery-info499 smoove.js http://www.lanr ...

  7. 第一次接触php

    一.什么是PHP PHP的中文意思:超文本预处理器,英文名字: HyperText Preprocessor. PHP通常有两层含义: (1)PHP是一个编程语言. (2)PHP是处理PHP编程语言的 ...

  8. TOJ 3750: 二分查找

    3750: 二分查找   Time Limit(Common/Java):3000MS/9000MS     Memory Limit:65536KByteTotal Submit: 1925     ...

  9. c++ 运算符重载operator

    一般格式为: 函数类型 operator 运算符名称(形参列表){ 对运算符的重载 } 注意函数名是由operator和运算符组成.在上面的一般格式中,operator是关键字,是专门用于重载运算符函 ...

  10. excel模板解析前后设计变化,以及我对此的看法和感受

    前言:近期也在做Excel模板的解析工作,目前来说,应该是第三版了.我最开始做的,就是垒鸡窝,虽然考虑了1.0提出的关于excel解析的一些建议和问题(单个模板),但是真的毫无设计可言.就几个工具类, ...