POJ 2411 Mondriaan's Dream [经典状态压缩dp]
题意:略。
思路:这一题开始做的时候完全没有思路,便去看了别人的题解。
首先,对于这个题目解法想有一个初步的了解,请看这里: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]的更多相关文章
- poj 2411 Mondriaan's Dream(状态压缩dp)
Description Squares and rectangles fascinated the famous Dutch painter Piet Mondriaan. One night, af ...
- [poj 2411]Mondriaan's Dream (状压dp)
Mondriaan's Dream Time Limit: 3000MS Memory Limit: 65536K Total Submissions: 18903 Accepted: 10779 D ...
- POJ 2411 Mondriaan's Dream (状压DP,骨牌覆盖,经典)
题意: 用一个2*1的骨牌来覆盖一个n*m的矩形,问有多少种方案?(1<=n,m<=11) 思路: 很经典的题目,如果n和m都是奇数,那么答案为0.同uva11270这道题. 只需要m个b ...
- POJ 2411 Mondriaan's Dream/[二进制状压DP]
题目链接[http://poj.org/problem?id=2411] 题意:给出一个h*w的矩形1<=h,w<=11.用1*2和2*1的小矩形去填满这个h*w的矩形,问有多少种方法? ...
- POJ 2411 Mondriaan's Dream 【状压Dp】 By cellur925
题目传送门 这道题暑假做的时候太模糊了,以前的那篇题解大家就别看了==.今天再复习状压感觉自己当时在写些什么鸭.... 题目大意:给你一个\(n\)*\(m\)的棋盘和许多\(1*2\)的骨牌,骨牌可 ...
- Poj 2411 Mondriaan's Dream(压缩矩阵DP)
一.Description Squares and rectangles fascinated the famous Dutch painter Piet Mondriaan. One night, ...
- POJ 2411 Mondriaan's Dream 插头dp
题目链接: http://poj.org/problem?id=2411 Mondriaan's Dream Time Limit: 3000MSMemory Limit: 65536K 问题描述 S ...
- POJ 2411 Mondriaan's Dream
思路:状态压缩dp,如果在(i,j)位置横着放砖块,那么(i,j)和(i+1.j)都是1,如果竖着放砖块,那么(i,j)为0,(i,j+1)为1,这样每行就可以用一个整数来存放状态,设dp[i][j] ...
- POJ 2411.Mondriaan's Dream 解题报告
题意: 给出n*m (1≤n.m≤11)的方格棋盘,用1*2的长方形骨牌不重叠地覆盖这个棋盘,求覆盖满的方案数. Solution: 位运算+状态压缩+dp ...
随机推荐
- vba通过函数调用Winrar压缩软件压缩文件
Dim site As String Dim FolderName As String Dim FolderName1 As String Dim Rarex ...
- 03013_动态页面技术-JSP
1.jsp的出现 2.jsp脚本和注释 (1)jsp脚本 ①<%java代码%> ----- 内部的java代码翻译到service方法的内部: ②<%=java变量或表达式> ...
- leetcode 【 Trapping Rain Water 】python 实现
题目: Given n non-negative integers representing an elevation map where the width of each bar is 1, co ...
- ios开发第一步--虚拟机安装MAC OS X
暂时还没买Macbook,先用虚拟机练练手. 先说说准备工作,我是在win8下安装的,这个不是关键的,只要Vmware版本和MAC OS X版本确定就行了,win7下同样可以. 1.虚拟机Vmware ...
- Python/PHP 远程文件/图片 下载
php 实现远程图片下载并保存到本地 /* *功能:php完美实现下载远程图片保存到本地 *参数:文件url,保存文件目录,保存文件名称,使用的下载方式 *当保存文件名称为空时则使用远程文件原来的名称 ...
- Leetcode 623.在二叉树中增加一行
在二叉树中增加一行 给定一个二叉树,根节点为第1层,深度为 1.在其第 d 层追加一行值为 v 的节点. 添加规则:给定一个深度值 d (正整数),针对深度为 d-1 层的每一非空节点 N,为 N 创 ...
- Notadd 2.0 全新 Node.js 版本~ (开发中) [从 PHP 到 node 的踩坑记]
对于 Notadd 我们本来期望它实现更多... 尽管我们也尝试做了很多努力,但是由于 PHP 本身的局限,以及考虑到开发环境配置的复杂程度,最终使用了折中方案.接下来,我们谈谈整个技术选型历程,也供 ...
- maven学习(十一)——maven中的聚合与继承
一.聚合 如果我们想一次构建多个项目模块,那我们就需要对多个项目模块进行聚合 1.1.聚合配置代码 <modules> <module>模块一</module> & ...
- CLion 使用笔记
我并不会写 CMakeFiles . 我要对拍,所以需要按下 Ctrl + F9 后同时编译 main.cpp 和 ref.cpp 两个文件,并生成 main.exe 和 ref.exe 以备进一步处 ...
- serviceImpl中,方法加@Override注释后报错
@Override public List<SysAdminMenu> getAdminMenusAll() { return sysAdminMenuMapper.getAdminMen ...