题目来源:http://poj.org/problem?id=1038

题目大意:

  有一家芯片公司要在一块N*M的板子上嵌入芯片,其中1<=N<=150, 1<=M<=10,但是板子上有一些格子是坏的,不能放置芯片。芯片的面积是2*3,可以横着放也可以竖着放,但不能有重叠。如下图所示:

现给出N M和坏点的坐标,求最多能在板子上嵌入多少芯片。

输入:第一行测试用例数,每例第一行三个整数N、M和K,K为坏点数。接下来K行,每行一个坏点坐标(n,m),1<=n<=N,1<=m<M,0<=K<=N*M.

输出:每组输入对应一行,输出能嵌入的最大芯片数目。


Sample Input

2
6 6 5
1 4
4 6
2 2
3 6
6 4
6 5 4
3 3
6 1
6 2
6 4

Sample Output

3
4

对于没有过竞赛经验的算法弱菜来说,这题是目前为止耗费掉了最多脑细胞的题,看了题解的”状态压缩DP“,小菜表示闻所未闻,实在太高级了,汗颜。结合网上看的解题报告和”黑书“里面的各种晦涩讲解,把自己理解的一种解题思路(更准确的说是解题方法吧,因为这思路我现在看来也是神一般的)写在下面吧:

黑书上说:观察后发现,N和M的范围相去甚远,同时总数据规模较大。这就给与提示:此题很可能是需要信息加密的动态规划,即把一行看成一个整体。

[画外音:第一,对于我这种弱菜来说,显然没有感受到这种”很可能“;第二,该书整文中对行列的描述与图片与题目图片里的行列方向是不一样的,但是题目里面给出的坐标的x和y的表示是x->N, 表行号,y->M,表列号,加上网上结题报告各种行列的错乱让小菜一开始晕头转向@_@...  所以,在本文中,为了尽量避免再出现这样的问题,辛苦读者在大脑中把上面的图逆时针旋转90°,约定M表示列数,N表示行数,方向(旋转后)约定为左->右,列号1->M,下->上,行号1->N.]

好了,回到重点。由于芯片的尺寸为2*3,所以我们能否在板子上放置一块芯片,使得该芯片的左上角所在格子的坐标为[x,y],只与[x, y]所在行和与它相邻的下面两行的占用状态(包括是否为坏点和是否被芯片占用两种情况)有关。由此启发,为每个格子设计一个状态表示方法(共三种状态,如图所示),来表示对它上一行相邻的格子能够作为芯片左上角位置的影响。

设当前格为[x,y],

0:表示[x,y]和[x,y-1]都为空闲

1:表示[x,y]空闲, [x,y-1]被占用(为坏点或者被其它芯片占用)

2:表示[x,y]被占用,此时无论[x,y-1]是否被占用,[x,y+1]处的格子都不可能成为某块芯片的左上角,所以不用管它的占用情况了

设计好了单个格子的状态表示之后,为了进行状态压缩,把一行中表示各个格子的状态值(第i列的格子状态值称p[i])组成的序列(看成是一个三进制串p[1]p[2]...p[M])换算成一个10进制数state,用来表示这一行的状态。一行有M格,每格有3种可能的状态,那么一行总共有3^M种状态。M的最大值为10,所以,一行最多的状态数为3^10=59049。

设计好了行的状态表示,接下来需要设计dp的状态和状态转移方程(各种状态@_@).

二维dp表中dp[i][j]表示当第i行的状态为j时,前i行放置芯片的最大数量。0<=i<=N, 0<=j<=3^M. 这样,就可以用当前行的状态推出上一行的放置情况(因为行状态j已经包含了两行格子的状态信息,所以有dp[i]足以推出dp[i+1]的信息, 这是后面使用滚动数组的基础)。

初始时把dp表的所有元素初始化为-1,表示该状态不可达或尚未开始考虑。

由一行的各列格子状态推出上一行的该列格子状态的规律是:q[i] = (p[i] == 0 ? 0 : p[i]-1); (q表示相邻上一行的格子的状态,分别试试三种情况就知道了)此时,还没有考虑上一行是否遇到坏点,所以当上一行这一列遇到了坏点,则q[i]应该变为2.

接下来是状态转移方程,考虑格子[i, y]时,有三种情况:

1. [i,y]不放芯片,转到[i,y+1];

2. [i,y]放纵向芯片,需要纵向的6个空格,所以条件为p[y] == 0 && p[y+1] == 0 && q[y] == 0 && q[y+1] == 0,更新dp[i+1][j'],j'为放了该块芯片后的i+1行状态,转到[i,y+2];

3. [i,y]放横向芯片,需要横向的6个空格,所以条件为y+2 <= M && q[y] == 0 && q[y+1] == 0 && q[y+2] == 0,更新dp[i+1][j'],j'为放了该块芯片后的i+1行状态,转到[i,y+3];

至此,dp的过程已经介绍完了(不知道讲清楚了几成=_=b),另外的一点优化就是上面提到的由于dp表一行一行更新时不需要前面更多行的信息,所以可以使用滚动数组来压缩存储,即循环利用数组的空间。

另一个优化在于:基于”加快经常性事件“的思想,本题里3进制和10进制的转换是频繁事件,选择高效的转换方法可以大大提高程序效率,由于位数确定且较少,可以预先计算好各位的权重。

 //////////////////////////////////////////////////////////////////////////
// POJ1038 Bugs Integrated, Inc.
// Memory: 720K Time: 969MS
// Language: C++ Result: Accepted
////////////////////////////////////////////////////////////////////////// #include <iostream> using namespace std; int N, M, k;
int d[][];
int w[] = {, , , , , , , , , , , };
int p[];
int q[];
int badSquares[][];
int cnt;
int o; inline int ternary2decimal(int a[]) {
int sum = ;
for (int i = ; i <= M; ++i) {
sum += a[i] * w[i];
}
return sum;
}
inline void decimal2ternary(int code, int a[]) {
for (int i = ; i <=M; ++i) {
a[i] = code % ;
code /= ;
}
} void dfs(int p[], int q[], int y, int cntNow) {
int qState = ternary2decimal(q);
if (cntNow > d[ - o][qState]) {
d[ - o][qState] = cntNow;
}
if (cntNow > cnt) {
cnt = cntNow;
}
for (int t = y; t < M; ++t) {
if (q[t] == && q[t + ] == && p[t] == && p[t + ] == ) {
q[t] = q[t + ] = ; //放置一块纵向芯片
dfs(p, q, t + , cntNow + ); //转至右边第二块
q[t] = q[t + ] = ;
} else if (t + <= M && q[t] == && q[t + ] == && q[t + ] == ) {
q[t] = q[t + ] = q[t + ] = ; //放置一块横向芯片
dfs(p, q, t + , cntNow + ); //转至右边第三块
q[t] = q[t + ] = q[t + ] = ;
}
}
} int main(void) {
int cases;
cin >> cases;
while(cases--) {
cin >> N >> M >> k;
memset(d, -, sizeof(d));
memset(badSquares, , sizeof(badSquares));
for (int i = ; i < k; ++i) {
int x, y;
cin >> x >> y;
badSquares[x][y] = ;
}
for (int i = ; i <= M; ++i) {
p[i] = badSquares[][i] + ;
//预先处理第一行的状态,坏点为2,
//好点为1(可以看做人工加入了全部是坏点的第0行,
//省去了下边界处理的问题)
}
d[][ternary2decimal(p)] = ; //前0行可放芯片数0
o = ;
cnt = ;
for (int i = ; i < N; ++i) {
memset(d[ - o], -, sizeof(d[ - o]));
for (int j = ; j < w[M + ]; ++j) {
if (d[o][j] == -) {
continue;
}
decimal2ternary(j, p);
for (int k = ; k <= M; ++k) {
q[k] = p[k] == ? : p[k] - ; //推上一行的每格状态
if (badSquares[i + ][k] == ) {
q[k] = ; //遇上坏点,状态为2
}
}
dfs(p, q, , d[o][j]);
}
o = - o;
}
printf("%d\n", cnt);
}
system("pause");
return ;
}

POJ1038 Bugs Integrated, Inc.的更多相关文章

  1. POJ1038 - Bugs Integrated, Inc.(状态压缩DP)

    题目大意 要求你在N*M大小的主板上嵌入2*3大小的芯片,不能够在损坏的格子放置,问最多能够嵌入多少块芯片? 题解 妈蛋,这道题折腾了好久,黑书上的讲解看了好几遍才稍微有点眉目(智商捉急),接着看了网 ...

  2. POJ1038 Bugs Integrated, Inc 状压DP+优化

    (1) 最简单的4^10*N的枚举(理论上20%) (2) 优化优化200^3*N的枚举(理论上至少50%) (3) Dfs优化状压dp O(我不知道,反正过不了,需要再优化)(理论上80%) (4) ...

  3. poj1038 Bugs Integrated,Inc. (状压dp)

    题意:N*M的矩阵,矩阵中有一些坏格子,要在好格子里铺2*3或3*2的地砖,问最多能铺多少个. 我的方法好像和网上流传的方法不太一样...不管了.... 由数据范围很容易想到状压dp 我们设某个状态的 ...

  4. POJ-1038 Bugs Integrated, Inc. (状压+滚动数组+深搜 的动态规划)

    本题的题眼很明显,N (1 <= N <= 150), M (1 <= M <= 10),摆明了是想让你用状态压缩dp. 整个思路如下:由于要填2*3或者3*2的芯片,那么就要 ...

  5. POJ 1038 Bugs Integrated, Inc.(DFS + 三进制状压 + 滚动数组 思维)题解

    题意:n*m方格,有些格子有黑点,问你最多裁处几张2 * 3(3 * 2)的无黑点格子. 思路:我们放置2 * 3格子时可以把状态压缩到三进制: 关于状压:POJ-1038 Bugs Integrat ...

  6. Bugs Integrated, Inc.

    Bugs Integrated, Inc. 给出一个\(n\times m\)的矩形网格图,给出其中K个障碍物的位置,求其中最多能摆的\(2\times 3\)的矩形的个数,\(n\leq 150,m ...

  7. 【CEOI2002】【Poj 1038】Bugs Integrated, Inc.

    http://poj.org/problem?id=1038 发一下中文题面(今天考试直接被改了): 生记茶餐厅由于受杀人事件的影响,生意日渐冷清,不得不暂时歇业.四喜赋闲在家,整天抱着零食看电视,在 ...

  8. POJ 1038 Bugs Integrated Inc (复杂的状压DP)

    $ POJ~1038~~\times Bugs~Integrated~Inc: $ (复杂的状压DP) $ solution: $ 很纠结的一道题目,写了大半天,就想练练手,结果这手生的.其实根据之前 ...

  9. POJ 1038 Bugs Integrated, Inc.

    AC通道 神坑的一道题,写了三遍. 两点半开始写的, 第一遍是直接维护两行的二进制.理论上是没问题的,看POJ discuss 上也有人实现了,但是我敲完后准备开始调了.然后就莫名其妙的以为会超时,就 ...

随机推荐

  1. mysql的UseAffectedRows问题 以及其他常见配置说明

    遇到MySQL中on duplicate key update语句返回值不正确: 在server5.1.*的返回分别为insert=1,update=3,nochange=2 在server5.5.* ...

  2. 2018.06.30 cdq分治

    #cdq分治 ##一种奇妙的分治方法 优点:可以顶替复杂的高级数据结构:常数比较小. 缺点:必须离线操作. CDQ分治的基本思想十分简单.如下: 我们要解决一系列问题,包含修改和查询操作,我们将这些问 ...

  3. 2018.08.04 洛谷P3380 【模板】二逼平衡树(树套树)

    传送门 技不如人,写的权值线段树套线段树在bzoj上无论如何都卡不过空间. 这是一道树套树简单题,感觉没什么好说的. 直接权值线段树套平衡树就行了. 代码: #include<bits/stdc ...

  4. addEvent兼容版

    function addEvent(elem,type,handle){ if (elem.addEventlistener) { elem.addEventlistener(type,handle, ...

  5. Git 同步远程仓库

    在你经常使用的命令当中有一个git branch –a 用来查看所有的分支,包括本地和远程的.但是时间长了你会发现有些分支在远程其实早就被删除了,但是在你本地依然可以看见这些被删除的分支. 同步远程分 ...

  6. oracle创建新的用户 创建序列 并生成自动自增

    1.用有dba权限的用户登录:sys用户 system 口令:manager 2.创建一个新用户:create user abc identified by 123456; 3.授予DBA权限: gr ...

  7. 20155302 2016-2017-2 《Java程序设计》第六周学习总结

    20155302 2016-2017-2 <Java程序设计>第6周学习总结 教材学习内容总结 Java中的流分为两种,一种是字节流,另一种是字符流,分别由四个抽象类来表示(每种流包括输入 ...

  8. 20155323 2016-2017-2 《Java程序设计》第6周学习总结

    20155323 2016-2017-2 <Java程序设计>第6周学习总结 教材学习内容总结 串流:衔接数据的来源和目的地就是串流对象. I/O操作主要是指使用Java进行输入,输出操作 ...

  9. Hdu1896 Stones(优先队列) 2017-01-17 13:07 40人阅读 评论(0) 收藏

    Stones Time Limit : 5000/3000ms (Java/Other)   Memory Limit : 65535/32768K (Java/Other) Total Submis ...

  10. 分离 桂林电子科技大学第三届ACM程序设计竞赛

    链接:https://ac.nowcoder.com/acm/contest/558/H 来源:牛客网 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 262144K,其他语言5242 ...