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 ...
随机推荐
- 《鸟哥的Linux私房菜》学习笔记(9)——条件判断
一.条件判断表达式 条件测试类型: 整数测试 字符测试 文件测试 条件测试的表达式 [ ...
- Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment
1.效果预览 1.1.这个首页就是一个Fragment碎片,本文讲述的就是这个碎片的搭建方式. 下拉会有一个旋转的刷新圈,上拉会刷新数据. 1.2.整体结构 首先底层的是BaseFragment 然后 ...
- Leetcode 640.求解方程
求解方程 求解一个给定的方程,将x以字符串"x=#value"的形式返回.该方程仅包含'+',' - '操作,变量 x 和其对应系数. 如果方程没有解,请返回"No so ...
- Matlab freqs 函数
freqs 模拟滤波器的频率响应 语法: h = freqs(b,a,w)[h,w] = freqs(b,a)[h,w] = freqs(b,a,f)freqs(b,a) 描述: freqs 返回一个 ...
- Vim插件YCM的安装
YouCompleteMe(YCM)是一款非常好用的Vim插件,但是很多人安装的时候会出问题(尤其是涉及到C和C++的补全),我安装的时候也遇到了问题,现在解决了,给大家参考: Step1: 通过Vu ...
- Hall 定理
Hall 定理 是匈牙利算法的基础 大意是说,对于一个二分图 左边的集合记为X,右边的集合记为Y 存在完美匹配,(即匹配数目=min(|X|,|Y|))的充分必要条件是 对于任意一个X的子集,设大小为 ...
- BZOJ2555 SubString 【后缀自动机 + LCT】
题目 懒得写背景了,给你一个字符串init,要求你支持两个操作 (1):在当前字符串的后面插入一个字符串 (2):询问字符串s在当前字符串中出现了几次?(作为连续子串) 你必须在线支持这些操作. 输入 ...
- drools6
<dependency> <groupId>org.drools</groupId> <artifactId>drools-core</artif ...
- 洛谷P1816 忠诚
P1816 忠诚 569通过 1.5K提交 题目提供者该用户不存在 标签云端 难度普及+/提高 时空限制1s / 128MB 提交 讨论 题解 最新讨论更多讨论 主席树的常数貌似大于线段树… TL ...
- ofbiz16 idea 启动
1.下载gradle并安装到本地 2.idea引入gradle 3.gradle右键选择refresh,项目会重新编译并加载gradle的task 4.可以再编译一下 5.没问题的话打开,jar ap ...