题目来源: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. 2018.10.02 NOIP模拟 聚会(前缀和)

    传送门 今天的签到题. 直接前缀和处理一下就秒了. 然而考试的时候智障用线段树维护被卡成了30分,交到OJ一测竟然有100? 搞得我都快生无可恋了. 如果用线段树来做可以类比这道题的写法,直接维护区间 ...

  2. 2018.08.19 NOIP模拟 change(简单模拟)

    Change 题目背景 SOURCE:NOIP2015-SHY-10 题目描述 Alice 和 Bob 又聚在一起了!他们已经厌倦了取石子游戏,现在他们热衷于切题.于是,Alice 找到了一道题让 B ...

  3. [MySQL]变更数据库字符集

    my.cnf [mysqld] character-set-server=utf8 [mysqld_safe] default-character-set=utf8 -- 创建数据库时,设置数据库的编 ...

  4. 如何使用Java执行cmd命令

    用JAVA代码实现执行CMD命令的方法! Runtime rt = Runtime.getRuntime(); Process p = rt.exec(String[] cmdarray);     ...

  5. SSH整合 第五篇 struts2的到来

    struts2的好处,web层的显示,同时Action类相当于MVC模式的C.整合进来的话,是通过与Spring整合,减少重复代码,利用IoC和AOP. 1.struts-2.5.2.jar 以上是s ...

  6. 求解1^2+2^2+3^2+4^2+...+n^2的方法(求解1平方加2平方加3平方...加n平方的和)

    利用公式 (n-1)3 = n3 -3n2 +3n-1 设 S3 = 13 +23 +33 +43 +...+n3 及 S2 = 12 +22 +32 +42 +...+n2 及 S1 = 1 +2 ...

  7. 类之间关系理解:组合>聚合>关联>依赖;实现,继承

    类之间关系理解:组合>聚合>关联>依赖:实现,继承 1. 从类之间的关系来看,不外乎以下几种 组合>聚合>关联>依赖:实现,继承 且可以分为以下两类: (1)实现, ...

  8. http://www.cnblogs.com/xalion/p/5111279.html

    https://quality.embarcadero.com/secure/Dashboard.jspa 注册成 delphi qc

  9. Android SQLiteOpenHelper Sqlite数据库的创建与打开

    Android Sqlite数据库是一个怎样的数据库? 答:是一种嵌入式小型设备,移动设备,的数据库,应用在穿戴设备(例如:智能手表,计算手环 等等),移动设备(例如:Android系统类型的手机 等 ...

  10. C# 获取相对路径(绝对路径转相对路径)

    这个的方法有很多吧. 1. 用PInvok调用Windows API的PathRelativePathTo 2. 自行处理字符串 3. 利用Uri 前两种就不说了,觉得有点麻烦,想了解的同学,自已,百 ...