题目描述 Description

用1*2的瓷砖去铺N*M的地面,问有多少种铺法

输入描述 Input Description

第一行有两数n,m。表示地面的大小

输出描述 Output Description

共一行,为方案总数

样例输入 Sample Input

2 2

样例输出 Sample Output

2

分析:

用1*2的砖去恰好铺满n*m的空间,对于第k行第j列,有3种情况将该点铺满
1:由第k-1行第j列砖竖着铺将第k行第j列铺满
2:由第k行第j列被横铺砖铺满
3:第k行第j列砖竖着铺将该点铺满
所以对于每一列的情况其实有两种(1,0)表示该点铺砖还是不铺
而对于每一列必须到达的状态只有一种,就是被铺满(1)
但是由上述3种情况将铺满方式分成两种:
0和1表示被k-1行j列竖铺铺满和在k-1行被横铺铺满
对于每一行列举每一种到达的状态j,dp[j]表示到达该状态有多少种情况
分析对于第k-1行状态j:10000111
需要到达第k行状态i: 01111011
如果需要到达第k行j列状态是0,则必须第k-1行该点状态不能是0,否则一定是连续两列竖放冲突
所以到达第k-1行该点只能是1,也就是说i|j一定每一位是1,也可以一步步判断是否满足第k行j列是0第k-1行j列是1
如果需要到达第k行状态j列是1,则假如第k-1行该点是0,则该点状态可以到达,继续判断j+1列
假如第k-1行该点是1,则第k行j列的1一定是横铺到达的,所以k行第j+1列一定也被铺满为1
从而第k-1行j+1列一定不能竖铺,必须被横铺铺满,所以也是1.
于是综合的第k行j列和第k-1行j列的关系(每一行每一列都表示到达的状态)
1:下面这种情况从第j列继续去判断j+1列
1
0
2:下面这种情况从第j列继续去判断j+1列
0
1
3:下面这种情况从第j列判断第j+1列是否全是1,然后继续判断第j+2列
1
1

*/
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <queue>
#include <algorithm>
#include <map>
#include <cmath>
#include <iomanip>
#define INF 99999999
typedef long long LL;
using namespace std; const int MAX=(<<)+;
int n,m;
LL temp[MAX],dp[MAX],bin[];
bool mark[MAX]; bool check(int i){
while(i){
if(i&){
i>>=;
if(!(i&))return false;//第j列是1则第j+1列必须是1
i>>=;//继续判断下一列
}else i>>=;//继续判断下一列
}
return true;
} void Init(){
memset(mark,false,sizeof mark);
memset(temp,,sizeof temp);
for(int i=;i<bin[m];++i){//初始化第一行和可以到达什么状态
if(check(i))temp[i]=,mark[i]=true;
}
} void DP(){
for(int k=;k<=n;++k){
for(int i=;i<bin[m];++i)dp[i]=;
for(int i=;i<bin[m];++i){
for(int j=;j<bin[m];++j){
if((i|j) != bin[m]-)continue;//每一位或之后必须每一位是1(综合前面3种情况和分析可知)
if(!mark[i&j])continue;//由初始化和前面分析三种情况分析可知i&j必须得到和初始化可以到达的状态一样才行
dp[i]+=temp[j];//i可以从j到达,则增加j的方案数
}
}
for(int i=;i<bin[m];++i)temp[i]=dp[i];
}
} int main(){
bin[]=;
for(int i=;i<;++i)bin[i]=*bin[i-];
while(~scanf("%d%d",&n,&m),n+m){
if(n<m)swap(n,m);//始终保持m<n,提高效率
Init();
DP();
printf("%lld\n",temp[bin[m]-]);//输出最后一行到达时的状态必须全部是1
}
return ;
}

优化:

<code class="language-cpp">/*优化:不去盲目的列举所有状态i和j然后判断状态j能否到达i,这样效率很低,因为能到达i的状态j很少
因此对于每种状态i,由i区搜索能到达i的状态j,大大提高效率
有298ms->32ms
*/
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <queue>
#include <algorithm>
#include <map>
#include <cmath>
#include <iomanip>
#define INF 99999999
typedef long long LL;
using namespace std; const int MAX=(<<)+;
int n,m;
LL temp[MAX],dp[MAX],bin[]; bool check(int i){
while(i){
if(i&){
i>>=;
if(!(i&))return false;//第j列是1则第j+1列必须是1
i>>=;//继续判断下一列
}else i>>=;//继续判断下一列
}
return true;
} void Init(){
memset(temp,,sizeof temp);
for(int i=;i<bin[m];++i)if(check(i))temp[i]=;//初始化第一行
} void dfs(int k,int i,int j){
if(k == m){dp[i]+=temp[j];return;}
if(k>m)return;
if((i>>k)&){
dfs(k+,i,j);
if((i>>(k+))&)dfs(k+,i,j|(<<k)|(<<(k+)));
}
else dfs(k+,i,j|(<<k));
} void DP(){
for(int k=;k<=n;++k){
for(int i=;i<bin[m];++i)dp[i]=;
for(int i=;i<bin[m];++i)dfs(,i,);
for(int i=;i<bin[m];++i)temp[i]=dp[i];
}
} int main(){
bin[]=;
for(int i=;i<;++i)bin[i]=*bin[i-];
while(~scanf("%d%d",&n,&m),n+m){
if(n<m)swap(n,m);//始终保持m<n,提高效率
Init();
DP();
printf("%lld\n",temp[bin[m]-]);//输出最后一行到达时的状态必须全部是1
}
return ;
}</code>

瓷砖覆盖(状压DP)的更多相关文章

  1. POJ2411骨牌覆盖——状压dp

    题目:http://poj.org/problem?id=2411 状压dp.注意一下代码中标记的地方. #include<iostream> #include<cstdio> ...

  2. 棋盘覆盖 状压DP+矩阵快速幂

    题意:有一个m 行n 列的矩形方格棋盘,1 < = m< = 5,1=< n< =10^9,用1*2 的骨牌(可横放或竖放)完全覆盖,骨牌不能重叠,有多少种不同的覆盖的方法.你 ...

  3. [JZOJ3521]道路覆盖--状压DP

    题目链接 略略略 分析 首先一看到使得最低的高度最高就想到了二分,于是就转化成了一个是否可行的问题 发现这个\(k\)都很小,考虑使用状态压缩DP 但是我一开始发现似乎并不好设计状态...如果这个\( ...

  4. 【BZOJ4560】[JLoi2016]字符串覆盖 KMP+状压DP

    [BZOJ4560][JLoi2016]字符串覆盖 Description 字符串A有N个子串B1,B2,…,Bn.如果将这n个子串分别放在恰好一个它在A中出现的位置上(子串之间可以重叠)这样A中的若 ...

  5. HDU 1565 - 方格取数(1) - [状压DP][网络流 - 最大点权独立集和最小点权覆盖集]

    题目链接:https://cn.vjudge.net/problem/HDU-1565 Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 32 ...

  6. POJ2836 Rectangular Covering(状压DP)

    题目是平面上n个点,要用若干个矩形盖住它们,每个矩形上至少要包含2个点,问要用的矩形的面积和最少是多少. 容易反证得出每个矩形上四个角必定至少覆盖了两个点.然后就状压DP: dp[S]表示覆盖的点集为 ...

  7. HDU5731 Solid Dominoes Tilings 状压dp+状压容斥

    题意:给定n,m的矩阵,就是求稳定的骨牌完美覆盖,也就是相邻的两行或者两列都至少有一个骨牌 分析:第一步: 如果是单单求骨牌完美覆盖,请先去学基础的插头dp(其实也是基础的状压dp)骨牌覆盖 hiho ...

  8. 多米诺骨牌放置问题(状压DP)

    例题: 最近小A遇到了一个很有趣的问题: 现在有一个\(n\times m\)规格的桌面,我们希望用\(1 \times 2\)规格的多米诺骨牌将其覆盖. 例如,对于一个\(10 \times 11\ ...

  9. POJ 2411 Mondriaan's Dream -- 状压DP

    题目:Mondriaan's Dream 链接:http://poj.org/problem?id=2411 题意:用 1*2 的瓷砖去填 n*m 的地板,问有多少种填法. 思路: 很久很久以前便做过 ...

  10. [WC2008]游览计划(状压dp)

    题面太鬼畜不粘了. 题意就是给一张n*m的网格图,每个点有点权,有k个关键点,让你把这k个关键点连成一个联通快的最小代价. 题解 这题nmk都非常小,解法肯定是状压,比较一般的解法插头dp,但不太好写 ...

随机推荐

  1. C++ STL, next_permutation用法。

    next_permutation 将按字母表顺序生成给定序列的下一个较大的序列,直到整个序列为 #include"iostream" #include"algorithm ...

  2. 根据sid或sqlID查询SQL

    根据sid查询已经执行过的sql select sql_text from v$sqlarea a,v$session b where a.SQL_ID=b.PREV_SQL_ID and b.SID ...

  3. poj 2069 Super Star 模拟退火

    题目大意: 给定三位空间上的n(\(n \leq 30\))个点,求最小的球覆盖掉所有的点. 题解: 貌似我们可以用类似于二维平面中的随机增量法瞎搞一下 但是我不会怎么搞 所以我们模拟退火就好了啊QA ...

  4. Operating System-Kickoff:什么是操作系统&&操作系统的核心概念

    接下来会写一系列Operating System(操作系统)的文章,今天先开个头.本文主要内容: 什么是操作系统 操作系统的核心概念 程序=数据结构+算法 一.什么是操作系统 1.1 操作系统是对硬件 ...

  5. vmware的双网卡以及Pro的注册码

    DC/OS的master需要能够上外网而且能够和本地内网设备交互,于是打算在虚拟机上面做测试,于是调研了一下虚拟机的双网卡配置.   最推荐的方式是使用vmware的station,而不是player ...

  6. Netty组件

    一.Channel.EventLoop 和ChannelFuture 这些类合在一起,可以被认为是Netty 网络抽象的代表: Channel—Socket: EventLoop—控制流.多线程处理. ...

  7. Java常见设计模式之工厂模式

    工厂模式在我们日常的应用中应当算是比较广泛的一种设计模式了.今天让我们一起来学习一下,工厂的设计模式. 工厂模式在<Java与模式>中分为三类:     1)简单工厂模式(Simple F ...

  8. 弱网测试--使用fiddle进行弱网测试

    数据源于:http://blog.csdn.net/eleven521/article/details/19089671 弱网测试原理以及方法(一)一.为什么要进行弱网测试?按照移动特性,各种网络连接 ...

  9. word2010以上版本中快捷录入数学公式的方法(二)

    以前推荐的方法,随着方正飞翔网站上关闭了数学公式输入法的支持也不能不用了,现在再推荐一个可以在word2010以上版中快捷输入数学公式的方法,安装AxMath,一切问题都OK!我是直接购买的正版,25 ...

  10. 前端之css样式(选择器)

    一.css概述 CSS是Cascading Style Sheets的简称,中文称为层叠样式表,对html标签的渲染和布局 CSS 规则由两个主要的部分构成:选择器,以及一条或多条声明. 例如 二.c ...