题意:略。

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

首先,对于这个题目解法想有一个初步的了解,请看这里: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]相关推荐

  1. Mondriaan‘s Dream(状态压缩dp)

    问题 S: Mondriaan's Dream 时间限制: 1 Sec  内存限制: 128 MB 提交: 2  解决: 2 [提交] [状态] [讨论版] [命题人:admin] 题目描述 Squa ...

  2. POJ 2411 Mondriaan‘s Dream(最清楚好懂的状压DP讲解)(连通性状态压缩DP)

    poj 2411 Mondriaan's Dream(最清晰的状压DP解析) 闫氏DP大法好 我们这里是一列一列地来,因为是一个棋盘性的状态压缩DP,从哪个方向都一样 摆放的小方格总方案数 等价于 横 ...

  3. POJ 2411 Mondriaan's Dream(状态压缩DP)

    题目链接 早就见过这个题,开始以为有公式的,推了几次没推出,后来知道这个题是状态压缩DP.最近开始看状态压缩,本想试着解出来,但是这个比那个牛吃草复杂多了...位运算还是不是很熟练,这个题的解题报告有 ...

  4. POJ 2411: Mondriaan's Dream

    题目链接:http://poj.org/problem?id=2411 题意: 用1*2的骨牌填充一个n*m的网格. 求方案数. 算法: 这道题用普通的状态压缩DP的话,打表也可以过. 也就是用一个m ...

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

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

  6. POJ - 2411 Mondriaan's Dream(状压dp)

    题目链接:点击查看 题目大意:铺瓷砖 题目分析:经典之经典的状态压缩动态规划,具体的懒得说了,怠惰ing..一个月之前写的代码,还好当时注释做的好,现在稍微一看就能立马理解,码到博客上存着吧,三种方法 ...

  7. POJ 2411 Mondriaan's Dream

    题意:用1*2的瓷砖拼出m*n的矩形.问有多少种拼法. 解法:设d[i][j]表示第i行状态为j的情况下,最多能有多少种拼法,对于状态j,1表示为竖着放置的瓷砖且它横跨i和i+1两行,其余皆用0表示. ...

  8. POJ2411-Mondriaan's Dream【状态压缩dp】

    正题 题目链接:http://poj.org/problem?id=2411 题目大意 有n×mn×mn\times m的矩阵,用1×21×21\times 2的方块填满有多少种方法. 解题思路 用1 ...

  9. poj 2411 Mondriaan#39;s Dream 【dp】

    题目:poj 2411 Mondriaan's Dream 题意:给出一个n*m的矩阵,让你用1*2的矩阵铺满,然后问你最多由多少种不同的方案. 分析:这是一个比較经典的题目.网上各种牛B写法一大堆. ...

最新文章

  1. 查找数组中第二个最小元素
  2. dataframe的multiIndex在次级index上做筛选
  3. 基于visual Studio2013解决C语言竞赛题之1081shell排序
  4. 架构学习优秀网站整理
  5. MySQL+Amoeba实现数据库主从复制和读写分离
  6. 外卖和快递行业数据_下周一起,整治全面启动!锁定全市外卖、快递行业!
  7. 在给定约束下可以使用a,b和c形成的字符串数
  8. 补充小知识:文件句柄与文件标识符
  9. [转载] Java Challengers#1:JVM中的方法重载
  10. 时间复杂度为O(n)的计数排序算法
  11. 简单的Qt倒计时程序--番茄钟
  12. 安卓12解除进程限制的第三种方式:黑阈
  13. 运放参数的详细解释和分析-part2-如何测量输入偏置电流Ib和输入失调电流Ios
  14. CentOS 7 以太网卡配置文件代码含义(ifcfg)
  15. 使用yum下载文件报No module named urlgrabber.grabber错
  16. Win10计算机窗口空白,windows10语言栏丢失怎么办?win10语言栏显示空白的解决办法...
  17. spring中pom变黑 中间有一条黑线,不能用,怎么解决
  18. VHDL实现USART
  19. 双主机切换下导致的显示器闪动
  20. Java实现网页浏览次数

热门文章

  1. Solr的自动完成实现方式(第三部分:Suggester方式续)
  2. 重构授课班时间安排模块代码
  3. 中国互联网发展的两个瓶颈
  4. journal of systems science and complexity
  5. intimidated
  6. mysql错误:……is marked as crashed and should be repai
  7. 2017年热度最高的十大技术类技能
  8. 数据库事务的隔离级别简单总结
  9. Linux系统性能监控之6个vmstat和6个iostat命令
  10. Android RecyclerView设计通用Adapter