题意:略。

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

首先,对于这个题目解法想有一个初步的了解,请看这里: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. 安卓手机关闭底部键盘灯的方法(htc G11亲测有效)

    还在因为看电子书和看电影时键盘灯刺眼而苦恼吗?下面提供一个方法关闭键盘灯,让你轻松DIY! 1、手机必须先Root。使用RE管理器,按照这个路径,找到文件:brightness sys/devices ...

  2. OpenStack之各组件介绍

    OpenStack简介 OpenStack既是一个社区,也是一个项目和一个开源软件,它提供了一个部署云的操作平台或工具集.其宗旨在于:帮助组织运行为虚拟计算或存储服务的云,为公有云.私有云,也为大云. ...

  3. 隐匿攻击-ICMP

    ICMP隐蔽隧道从入门到精通 概述 众所周知传统socket隧道已极少,tcp.upd大量被防御系统拦截,dns.icmp.http/https等难于禁止的协议(当然还有各种xx over dns/i ...

  4. Python-S9-Day123——爬虫两示例

    01 今日内容回顾 02 内容回顾和补充:面向对象约束 03 爬虫之抽屉新热榜 04 爬虫之抽屉自动登录(一) 05 爬虫之抽屉自动登录(二) 06 爬虫之登录github(一) 07 爬虫之登录gi ...

  5. csu-2018年11月月赛Round2-div1题解

    csu-2018年11月月赛Round2-div1题解 A(2191):Wells的积木游戏 Description Wells有一堆N个积木,标号1~N,每个标号只出现一次 由于Wells是手残党, ...

  6. Atom-无懈可击的Markdown编辑器

    备战美赛期间,向岳神学习,搞了Atom玩协作开发,第一次没有自动补全的手撸了遗传算法.今天发现Atom还有写Markdown的妙用,遂拿来练手. 1. 安装Atom 下载安装Atom:https:// ...

  7. 单元测试如何保证了易用的API

    一般而言TDD的好处是以输出为导向及早发现问题,以及方便重构(单元测试保证).我理解,还有一个比较重要的意义是: 客观上强制了程序员写出更加友好的接口 方便测试和联调. 问题 这里我以c++举例,需求 ...

  8. Linux 基础命令、文档树 和 bash

    最近发现了一个总结得更好的:bash cheatsheet 本文只是我对 linux 基础学习的一个总结,可能仅适用于复习用.算是我的 Linux 备忘录. 最基础 tab 补全 * 通配符 ctrl ...

  9. [SDOI2009][bzoj1878] HH的项链 [莫队模板题]

    题面: 传送门 思路: 就是一道莫队的模板题目...... 开一个1000000的数组记录每个数出现的次数,然后每次从1到0或者从0到1更新答案 莫队讲解看这里:莫队 Code: #include&l ...

  10. SystemProperties反射调用

    Systemproperties类在android.os下,但这个类是隐藏的,上层程序开发无法直接使用,只能通过java反射来调用 SystemPropertiesInvoke.java packag ...