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的解法,我觉得很精巧,在下面重点分析。这里先贴下上面方法的代码。
1 #include<stdio.h> 2 #include<iostream> 3 #include<vector> 4 #include<string.h> 5 #include<algorithm> 6 using namespace std; 7 long long dp[1<<12][13]; 8 vector<int> ok[1<<12]; 9 int h, w; 10 bool judge(int up, int down) 11 { 12 int u[13], d[13]; 13 int now = w; 14 while (now) 15 { 16 u[now] = up % 2; 17 d[now--] = down % 2; 18 up /= 2; 19 down /= 2; 20 } 21 for (int i = 1; i <= w;) 22 { 23 if (!d[i])//该行该位为0 24 { 25 if (!u[i]) return 0;//上一行若也为0,则不合法 26 i++; 27 } 28 else if (!u[i])//该行该位为1,且上一行该位为0 29 i++; 30 else//该行该位为1,且上一行该位也为1 31 { 32 if (i + 1 > w || !d[i+1] || !u[i+1]) return 0; 33 i += 2; 34 } 35 } 36 return 1; 37 } 38 long long getdp() 39 { 40 memset(dp, 0, sizeof(dp)); 41 for (int i = 0; i < (1<<w); i++) 42 { 43 ok[i].clear(); 44 for (int j = 0; j < (1<<w); j++) if (judge(j, i)) 45 ok[i].push_back(j); 46 } 47 for (int i = 0; i < (1 << w); i++) 48 for (int j = 0; j < ok[i].size(); j++) if (ok[i][j] == (1<<w) - 1) 49 dp[i][1] = 1; 50 for (int i = 2; i <= h; i++) 51 for (int j = 0; j < (1 << w); j++) 52 for (int k = 0; k < ok[j].size(); k++) 53 dp[j][i] += dp[ ok[j][k] ][i-1]; 54 return dp[(1<<w)-1][h]; 55 } 56 int main() 57 { 58 while (~scanf("%d%d", &h, &w) && h && w) 59 { 60 if ((h * w) % 2) 61 { 62 printf("0\n"); 63 continue; 64 } 65 if (h < w) swap(h, w); 66 printf("%lld\n", getdp()); 67 } 68 return 0; 69 }
===========分割线===============
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值。
这种方法代码既短又巧妙,让人佩服啊。
1 #include<stdio.h> 2 #include<algorithm> 3 #include<string.h> 4 using namespace std; 5 long long dp[1<<12][13], tem, h, w; 6 void dfs(int row,int state,int pos) 7 { 8 if (pos == w) 9 { 10 dp[state][row] += tem; 11 return; 12 } 13 dfs(row, state, pos + 1); 14 if (pos <= w - 2 && !(state & (1<<pos)) && !(state & (1<<(pos + 1)))) 15 dfs(row, state | 1<<pos | 1<<(pos+1), pos + 2); 16 } 17 int main() 18 { 19 while (~scanf("%d%d", &h, &w) && h && w) 20 { 21 if (h * w % 2) 22 { 23 printf("0\n"); 24 continue; 25 } 26 if (h < w) swap(h, w); 27 memset(dp, 0, sizeof(dp)); 28 tem = 1; 29 dfs(1, 0, 0); 30 for (int i = 2; i <= h; i++) 31 for (int j = 0; j < (1<<w); j++) if (dp[j][i-1]) 32 { 33 tem = dp[j][i-1]; 34 dfs(i, ~j & ((1<<w) - 1), 0); 35 } 36 printf("%lld\n", dp[(1<<w)-1][h]); 37 } 38 return 0; 39 }
转载于:https://www.cnblogs.com/fenshen371/p/3269801.html
POJ 2411 Mondriaan's Dream [经典状态压缩dp]相关推荐
- Mondriaan‘s Dream(状态压缩dp)
问题 S: Mondriaan's Dream 时间限制: 1 Sec 内存限制: 128 MB 提交: 2 解决: 2 [提交] [状态] [讨论版] [命题人:admin] 题目描述 Squa ...
- POJ 2411 Mondriaan‘s Dream(最清楚好懂的状压DP讲解)(连通性状态压缩DP)
poj 2411 Mondriaan's Dream(最清晰的状压DP解析) 闫氏DP大法好 我们这里是一列一列地来,因为是一个棋盘性的状态压缩DP,从哪个方向都一样 摆放的小方格总方案数 等价于 横 ...
- POJ 2411 Mondriaan's Dream(状态压缩DP)
题目链接 早就见过这个题,开始以为有公式的,推了几次没推出,后来知道这个题是状态压缩DP.最近开始看状态压缩,本想试着解出来,但是这个比那个牛吃草复杂多了...位运算还是不是很熟练,这个题的解题报告有 ...
- POJ 2411: Mondriaan's Dream
题目链接:http://poj.org/problem?id=2411 题意: 用1*2的骨牌填充一个n*m的网格. 求方案数. 算法: 这道题用普通的状态压缩DP的话,打表也可以过. 也就是用一个m ...
- POJ 2411.Mondriaan's Dream 解题报告
题意: 给出n*m (1≤n.m≤11)的方格棋盘,用1*2的长方形骨牌不重叠地覆盖这个棋盘,求覆盖满的方案数. Solution: 位运算+状态压缩+dp ...
- POJ - 2411 Mondriaan's Dream(状压dp)
题目链接:点击查看 题目大意:铺瓷砖 题目分析:经典之经典的状态压缩动态规划,具体的懒得说了,怠惰ing..一个月之前写的代码,还好当时注释做的好,现在稍微一看就能立马理解,码到博客上存着吧,三种方法 ...
- POJ 2411 Mondriaan's Dream
题意:用1*2的瓷砖拼出m*n的矩形.问有多少种拼法. 解法:设d[i][j]表示第i行状态为j的情况下,最多能有多少种拼法,对于状态j,1表示为竖着放置的瓷砖且它横跨i和i+1两行,其余皆用0表示. ...
- POJ2411-Mondriaan's Dream【状态压缩dp】
正题 题目链接:http://poj.org/problem?id=2411 题目大意 有n×mn×mn\times m的矩阵,用1×21×21\times 2的方块填满有多少种方法. 解题思路 用1 ...
- poj 2411 Mondriaan#39;s Dream 【dp】
题目:poj 2411 Mondriaan's Dream 题意:给出一个n*m的矩阵,让你用1*2的矩阵铺满,然后问你最多由多少种不同的方案. 分析:这是一个比較经典的题目.网上各种牛B写法一大堆. ...
最新文章
- 查找数组中第二个最小元素
- dataframe的multiIndex在次级index上做筛选
- 基于visual Studio2013解决C语言竞赛题之1081shell排序
- 架构学习优秀网站整理
- MySQL+Amoeba实现数据库主从复制和读写分离
- 外卖和快递行业数据_下周一起,整治全面启动!锁定全市外卖、快递行业!
- 在给定约束下可以使用a,b和c形成的字符串数
- 补充小知识:文件句柄与文件标识符
- [转载] Java Challengers#1:JVM中的方法重载
- 时间复杂度为O(n)的计数排序算法
- 简单的Qt倒计时程序--番茄钟
- 安卓12解除进程限制的第三种方式:黑阈
- 运放参数的详细解释和分析-part2-如何测量输入偏置电流Ib和输入失调电流Ios
- CentOS 7 以太网卡配置文件代码含义(ifcfg)
- 使用yum下载文件报No module named urlgrabber.grabber错
- Win10计算机窗口空白,windows10语言栏丢失怎么办?win10语言栏显示空白的解决办法...
- spring中pom变黑 中间有一条黑线,不能用,怎么解决
- VHDL实现USART
- 双主机切换下导致的显示器闪动
- Java实现网页浏览次数