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 ...
随机推荐
- MySQL基础6-分组查询
1.分组函数 需求20:查询所有商品平均零售价SELECT AVG(salePrice) FROM product 需求21:查询商品总记录数SELECT COUNT(id) count FROM p ...
- 随手正则写的 CSDN【只看楼主】功能
写这个的时候居然没有看到原来CSDN已经有这个功能了,写完代码了突然发现原来早就已经有了. 现把代码贴出来吧,虽然有很多解析HTML的开源类库如:http://htmlagilitypack.code ...
- Django权限管理系统设计分析
权限管理顾名思义,其实就是角色控制权限的系统,每个用户对应一个角色,每个角色有对应的权限,比如公司会有CEO,总监,销售经理,销售员,每个人的权限都不一样,那我们给他展示的url也都不同 一.首先创建 ...
- requireJS入门学习
前言 最近网上.群里各种随便看,随便学.暑期实习还没找到,昨天开题过了,好好学习吧.最近一直看到前端的ADM,CMD规范,然后网上各种找资料看,看了好几个牛人的博客,写的很好,然后自我感觉了解了点,介 ...
- C 语言 习题 1-14
练习 1-14 编写一个程序,打印输入中各个字符出现频度的直方图. #include <stdio.h> /* count digits, white space, others */ i ...
- python学习-- settings 设置sqlserver连接
PyCharm 开发工具 先打开项目 1. ctrl+alt+s 2. project:项目名称 选中Project Interpreter,点右面+号 :搜索 django-pyodbc-az ...
- RESTful-rest_framework应用第二篇(get、post的序列化与反序列化)
目的是: 利用rest_framework实现对数据库内容的查看get请求(序列化).提交保存数据post请求 (反序列化) rest_framework序列化组件(即查看和) 第一步:基于Djang ...
- JAVA使用JDBC连接MySQL数据库 一
public class JDBCTest { public static void main(String[] args){ String driver = "com.mysql.jdbc ...
- HDU6438 Buy and Resell 解题报告(一个有趣的贪心问题的严格证明)
写在前面 此题是一个很容易想到的贪心题目,但是正确性的证明是非常复杂的.然而,目前网上所有题解并未给出本题贪心算法的任何正确性证明,全部仅停留在描述出一个贪心算法.本着对算法与计算机科学的热爱(逃), ...
- webpack编译vue出现dev警告
alias配置 vue < 2.1.0 module.exports = { ... resolve: { alias: { 'vue$': 'vue/dist/vue.js' } }, ... ...