[SMOJ1777]铺地砖
题目描述
有一个高度是 h h,宽度是 ww 的二维表格,你要用 1×2 1\times2 和 2×1 2\times1 的地砖把表格铺满,有多少种不同的方案?
如下是一些样例:
输入格式 1777.in
输入包含多个测试用例。每个测试用例是由两个整数数字:高度 h h 和大型矩形的宽度 ww。输入由 h=w=0 h = w = 0 时终止。否则, 1≤h 1 \leq h, w≤11 w \leq 11。
输出格式 1777.out
每个测试数据输出一个答案。
输入样例 1777.in
1 2
1 3
1 4
2 2
2 3
2 4
2 11
4 11
0 0
输出样例 1777.out
1
0
1
2
3
5
144
51205
这题据说还有组合数学和其他各种(如插头dp)做法,不过既然作为状压 DP 的题目,我们还是考虑一下状压 DP 的做法。
首先,无论怎么铺,一定是要铺满的,这是重点。那么我们的状态中 0 和 1 就不可能表示“空”或“铺”了,而应该是考虑到另一个方面——铺法。
(也可以这么说:不是铺不铺的问题,而是怎么铺的问题。)
这题的基本铺法无非只有 1×2 1\times2 或 2×1 2\times1 两种,而要求的也就是不同的方案数。那么两种地砖 1×2 1\times2 或 2×1 2\times1,横的会影响左右(一行内相邻两列)、竖的会影响上下(一列内相邻两行)的取值。跟我们这几天做的其他一些 DP 类似,我们不妨设 f[i][set] f[i][set] 为铺满前 i i 行,其中第 ii 行的状态为 set set 的方案数。
关键是,我们要通过枚举两行的状态,消除限制。(这个要重点理解好)
状态转移时,有
f[i+1][k]\leftarrow f[i+1][k]+f[i][j]\text{ for each possible new state }\, k\text{ of }j
现在我们问题主要是如何判定方案的合法性,但先要解决状态所表示的含义。
我的处理方法是:在 f[i][set] f[i][set] 的 set set 中,0 表示某位“还有空间”(注意,不是不铺!),1 表示没有空间。那么在状态转移的时候,我是从当前行不断改进下一行的。现在就来集中精力考虑,比方说我们现在有一个 set set(例如101110),那么下一行肯定是?1???1。因为要满足铺满,所以右数第1位和第5位只能竖着放,剩下的 4 位有两种情况:暂时先不放,或横着放,这就是 set set 的合法状态。
那么我们其实就可以根据当前行已知状态,跑一遍 dfs,得到下一行的合法状态。反正数据范围小,怎么乱搞都无所谓啦。
解题细节:要开long long,多组数据 DP 数组记得清空。
最后还有,在跟同学交流的时候,有同学比较容易混乱。其实状压 DP 里面我们只要始终关注:0 和 1 的具体含义,状态的含义就可以了,不要混淆。
写初始化、状态转移和最终求答案的时候总是想一想:这里的状态是什么意思,应该怎么取值,再联系一下题目,应该怎么初始化、怎么转移、怎么求解,其实并不难。关键是要确定一种表示方法,0 和 1 的含义由始至终必须是唯一的,不然就会乱。
参考代码:
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>using namespace std;const int maxn = 15;int h, w;
long long dp[maxn][1 << maxn];int cnt;
int state[1 << maxn];void solve(int now, int k) { //dfs 求出所有可能的状态if (k + 1 == w) {state[cnt++] = now;return ;}solve(now, k + 1); //不放if (!(now & (1 << k)) && !(now & (1 << (k + 1)))) solve(now | (3 << k), k + 1); //无限制,横着放
}int main(void) {freopen("1777.in", "r", stdin);freopen("1777.out", "w", stdout);while (~scanf("%d%d", &h, &w)) {if (!h || !w) break;if (h * w & 1) { //特判puts("0");continue;}int upperLim = 1 << w;cnt = 0;solve(0, 0); //第一行的情况memset(dp, 0, sizeof dp);for (int i = 0; i < cnt; i++) dp[1][state[i]] = 1; //初始化for (int i = 1; i < h; i++)for (int j = 0; j < upperLim; j++) {cnt = 0;solve(upperLim - j - 1, 0);for (int k = 0; k < cnt; k++) dp[i + 1][state[k]] += dp[i][j];}printf("%lld\n", dp[h][upperLim - 1]);}return 0;
}
update:2017/4/13 讲评后
昨晚老师讲了另一种更简便的状态表示方法:0 表示没有限制地铺,不知道怎么铺,随便铺;1 表示这里已经有一个打竖铺的。
那么答案就是 f[n][0] f[n][0]。(无限制的所有方案数)
另外,在转移的时候,枚举第 i−1 i - 1行和第 i i 行状态,也就确定了它们对第 i+1i + 1 行的影响。设第 i−1 i - 1 行状态为 a a,第 ii 行状态为 b b,第 i+1i + 1 行为 c c,那么就有:c=(~b)&a
,这是位运算的基本功。解释一下:
例如 aa 是 11001(三个竖铺,两个不确定), b b 可能是 11001(注意 aa 必须为 b b 的子集,因为在 aa 中竖铺的格子在 b b 中也要竖铺,那么 cc 的取值就是:先把 b b 取反,变成了 00110,再跟 aa 并,变成了 00000(没有限制)。这是因为竖铺的格子都在 i i 行和 i−1i - 1 行了,对 i+1 i + 1 行就不造成影响了。
对于看似复杂的问题,通过转化就可变为动态规划问题。而不同的规划方法也会影响编程实现的复杂度。因此,做题时,想清楚再开始打代码。
[SMOJ1777]铺地砖相关推荐
- 靠数学“拿了”两次诺贝尔奖,彭罗斯从“铺地砖”帮忙发现2011年化学奖的秘密...
晓查 发自 凹非寺 量子位 报道 | 公众号 QbitAI 诺贝尔奖没有数学奖,但是如果数学足够好的话,可以拿两次诺贝尔奖: 帮别人拿一次,自己再拿一次. 刚刚获得诺贝尔奖的英国数学家罗杰·彭罗斯( ...
- 迷“铺地砖”的数学家
可以构成平面五重晶格铺砌的五种凸形砖 每当宗传明开始"闭关",谢绝一切会议.邀约时,熟悉他的人就能大致猜出,他又要"憋大招"了. 数学研究以抽象闻名,外行往往难 ...
- 青蛙过河 猴子爬山 兔子繁殖 开宝箱2 找气球 指针函数 铺地砖
Problem A: 青蛙过河 Description 一条小溪尺寸不大,青蛙可以从左岸跳到右岸,在左岸有一石柱L,面积只容得下一只青蛙落脚,同样右岸也有一石柱R,面积也只容得下一只青蛙落脚.有一队青 ...
- [2022.1.13]UPC-2021级新生个人训练赛第22场-9783 Problem H 铺地砖
问题 H: 铺地砖 时间限制: 1.000 Sec 内存限制: 128 M 题目描述 一天,晨晨的数学老师布置了一道题目,大意如下:用1×1和2×2的磁砖不重叠地铺满n×3的地板,共有多少种方案? 例 ...
- Problem C: 铺地砖——迭代小算法
搜了一下百度,网上很多人都对这题没有深入下去进行解释,大多数只是通过枚举法就得出结论,因此我写了这篇博客讲讲我对这个题目的理解. 首先这道题目通过枚举法可以发现从第四项开始,方法总数就是前三项的和了, ...
- 铺地砖(找递推式 + 大数)
铺地砖 时间限制: 1 Sec 内存限制: 128 MB 题目描述 一天,晨晨的数学老师布置了一道题目,大意如下:用1×1和2×2的磁砖不重叠地铺满n×3的地板,共有多少种方案? 例如:n=1时:1 ...
- Dynamo For Revit: 铺地砖
前言 本文介绍如何用 Dynamo 铺地砖,仅仅是演示版,把基本思想梳理一下. 地砖效果 可以适用于不规则的地块,地砖长和宽可以调整. Dynamo 脚本逻辑 要完成这个功能: 找到要铺地砖的平面,设 ...
- 背地砖上楼的机器人_德国发明铺地砖机器人,效率大幅度上升,节省千万!
小伙伴们应该都知道,再我们传统一点的建筑领域当中,给地面上铺砖可以说是一个十分考验技术的活了,不仅仅是需要我们拥有相当丰厚的经验还有技术,在工作的过程中我们还需要十分的细心,但是我们要是精益求精的话, ...
- 铺地砖问题 java
问题描述 有一长度为N(1<=N<=10)的地板,给定两种不同瓷砖:一种长度为1,另一种长度为2,数目不限. 要将这个长度为N的地板铺满,一共有多少种不同的铺法? import java ...
最新文章
- SKU表管理之更新SKU表数据
- 随办 企业打造完美执行团队的终极利器
- 高性能javascript 文件加载阻塞
- python在线课程-《Python程序设计与应用》在线课程使用说明
- CSP认证201509-3	模板生成系统[C++题解]:字符串处理、模拟、哈希表、引号里面有空格的字符串怎么读入
- Java客户端操作zookeeper:删除节点代码示例
- Go语言 - 忍不了了!!!已将Go语言各标准包文档.移到自己服务器上
- 解读三种虚拟化之路连载一:x86虚拟化概述
- spring实现模板文件下载
- linux网站爬取,Kali下httrack 爬取网站页面
- Linux设置RSA密钥登录
- 提升研发效率 保障数据安全——阿里云宣布数据管理DMS企业版正式商业化
- php的curl如何返回值,怎么在PHP中利用CURL获取返回值
- HDU 4336 概率DP 状压
- (65)Verilog HDL多模块重复例化:generate for
- 关于数字签名简要原理
- vs2008的简单使用
- java 微博 屏蔽_最新JAVA调用新浪微博API之发微博(转)
- [转]XXX无法访问。你可能没有权限使用网络资源
- 从原理上搞懂编码——究竟什么是编码?什么是解码?什么是字节流?
热门文章
- Apache配置错误日志
- matlab调用opensees,基于OpenSees与MATLAB的逐步增量动力分析与数据处理方法技术
- java计算机毕业设计智慧机场管理系统源程序+mysql+系统+lw文档+远程调试
- zip分卷压缩linux命令,linux下zip分卷压缩及linux下zip分卷解压
- 虚拟机运行win11以及安装apk
- web html做菜单,Web元素设计之导航菜单
- BAT职级薪酬深度分析
- nginx中proxy_set_header Host $host的作用
- ADPCM编解码的使用
- Kotlin 异常处理