本题的题眼很明显,N (1 <= N <= 150), M (1 <= M <= 10),摆明了是想让你用状态压缩dp。

整个思路如下:由于要填2*3或者3*2的芯片,那么就要找一个策略来判断到底能不能填。

精华1在此:

找到的策略是,记格子(i,j)的状态有三种:

状态0代表(i,j)和(i-1,j)均可用(可用包括非损坏和未占用)

状态1代表(i,j)可用但(i-1,j)不可用

状态2代表(i,j)和(i-1,j)均不可用。

这样设置状态后,我们可以将填芯片这个问题策略化描述:

即能够填芯片的情况无非两种:

其一横着填3*2的芯片,此时只需满足state(i,j)=0&&state(i,j+1)=0&&state(i,j+2)=0即可(在代码中用的是pre和now数组存放的两行状态)

其二是竖着填2*3的芯片,此时只需满足state(i,j)=0&&state(i,j+1)=0&&state(i-1,j)=0&&state(i-1,j+1)=0即可。

这样一来就可以从第一行开始深搜每一行可能出现的填芯片的情况并计数。

同时由于每个格子的状态只有3种,因此可以用3进制数对每一行的状态进行记录,也就是所谓的状态压缩。

比如如果某一行的状态为(1,1,0,2,2),转化为3进制数就是1*3^0+1*3^1+0*3^2+2*3^3+2*3^4。这样就将一行的状态用一个3进制数表示了。

精华2:在考虑完如何进行状态压缩后下一步就是如何用dp来求解了。

思路如下:既然可以将一行的状态记录为一个3进制数 t,那么递推关系不妨按行递推,也就是考虑从第一行开始,每增加一行,能填的芯片数目会增加多少。

而由精华1不难发现,只要知道新增行和上一行的状态,就可以推出填芯片的所有可能方案(这里显然需要搜索解决)。

因此考虑这样进行存储:设置一个dp[i][j]数组,其中i表示第几行,j表示该行当前的状态(也就是上文所说的压缩得到的3进制数)。

dp[i][j]的值表示以第i行为最后一行并且第i行状态为j的情况下所能填入的最大芯片数。

那么dp的目标就转化为求出所有dp[i][j]的值。

这里的特殊性在于,递推关系不再是一个简单的式子,而是通过深搜来更新的。

至此,思路就已经相当明确 了。

为了节约内存空间,在具体计算dp数组时用了滚动数组的方法(毕竟每次更新只需要保存两行状态就可以了)

一些小tips和犯过的错都记录在代码里了,以备温故知新。

AC代码如下:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m;
int map[][];
int dp[][];
int now[];
int pre[];
int t[]={,,,,,,,,,,,};//3进制权重
int getstate(int a[]){
int tem=;
for(int i=;i<=m;i++){
tem+=a[i]*t[i];
}
return tem;
}
int getstring(int tem,int*a){
for(int i=;i<=m;i++){
a[i]=tem%;
tem=tem/;
}
//注意一下,注释里面是一开始写的错误程序;
//这样写的错误在于 不能完整的修改字符串,无法正确处理高位若干0的情况
// int i=0;
// while(tem!=0){
// a[++i]=tem%3;
// tem/=3;
// }
} void dfs(int i,int j,int prenum,int state){//深搜的典型框架
dp[i%][state]=max(dp[i%][state],prenum);
//dp[i%2][state]=prenum;//写成这样是错的 ,道理很简单,每种状态是可能被重复搜索到的,而
//在重复搜索到的时候就必须选择最大的那种情况才满足题意 ,一个简单的例子就是当某一行更新后状态为(2,2,2,2,2,2)
//则可能有两种情况出现 ,一种是3个2*3的芯片,一种是2个3*2的芯片。
int k;
if(j>=m)return;
if(j<m-&&now[j]==&&now[j+]==&&now[j+]==){
now[j]=now[j+]=now[j+]=;
k=getstate(now);
dfs(i,j+,prenum+,k);
now[j]=now[j+]=now[j+]=;
}
if(pre[j]==&&pre[j+]==&&now[j]==&&now[j+]==){
now[j]=;now[j+]=;
k=getstate(now);
dfs(i,j+,prenum+,k);
now[j]=;now[j+]=;
}
dfs(i,j+,prenum,state);
return;
} int main(void){
int d;
scanf("%d",&d);
while(d--){
int k;
scanf("%d%d%d",&n,&m,&k);
memset(map,,sizeof(map)); while(k--){
int a,b;
scanf("%d%d",&a,&b);
map[a][b]=;
}
for(int i=;i<=t[m+];i++){//一开始写的i=1
dp[][i]=-;
}
for(int i=;i<=m;i++){
pre[i]=map[][i]+;
}
int state=getstate(pre);
dp[][state]=;
for(int i=;i<=n;i++){//这里写的时候思路不连贯
for(int j=;j<=t[m+];j++){
dp[i%][j]=-;
}
for(int j=;j<=t[m+];j++){
if(dp[(i+)%][j]==-)continue;
getstring(j,pre);
for(int k=;k<=m;k++){
if(map[i][k]==)now[k]=;
else if(pre[k]==)now[k]=;
else{
now[k]=;
}
}
state=getstate(now);
dfs(i,,dp[(i+)%][j],state);
}
}
int ans=;
for(int i=;i<=t[m+];i++){
ans=max(dp[n%][i],ans);
}
printf("%d\n",ans);
}
return ;
}

POJ-1038 Bugs Integrated, Inc. (状压+滚动数组+深搜 的动态规划)的更多相关文章

  1. POJ 1038 Bugs Integrated, Inc. ——状压DP

    状态压缩一下当前各格子以及上面总共放了几块,只有012三种情况,直接三进制保存即可. 然后转移的时候用搜索找出所有的状态进行转移. #include <map> #include < ...

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

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

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

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

  4. POJ 1038 Bugs Integrated, Inc.

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

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

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

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

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

  7. poj 1038 Bugs Integrated, Inc. 题解

    提供一种代码难度比较简单的做法(可能) 状态表示: 设置状态$ f[i][j] $,表示第 \(i\) 行状态为 \(j\) 的最大放置数,因为这是个阴间题,因为题目内存设置很小,所以要用滚动数组,存 ...

  8. poj 2288 Islands and Bridges ——状压DP

    题目:http://poj.org/problem?id=2288 状压挺明显的: 一开始写了(记忆化)搜索,但一直T: #include<iostream> #include<cs ...

  9. poj 2663 Tri Tiling (状压dp+多米诺骨牌问题+滚动数组反思)

    本来直接一波状压dpAC的 #include<cstdio> #include<cstring> #include<algorithm> #define REP(i ...

随机推荐

  1. Debug常用命令

    R命令 查看.修改CPU中寄存器的值 -r ;查看寄存器的值 -r cs ;修改cs寄存器的值 D命令 查看内存中的内容 ;d 段地址:偏移地址 -d 1000:01 ;查看内存100001处的内容 ...

  2. 水题C

    某校大门外长度为L的马路上有一排树,每两棵相邻的树之间的间隔都是1米.我们可以把马路看成一个数轴,马路的一端在数轴0的位置,另一端在L的位置:数轴上的每个整数点,即0,1,2,……,L,都种有一棵树. ...

  3. 如何让多个dz论坛共用一个用户数据库

    用户数据库在论坛中是可以独立备份的,备份方法:论坛后台——站长——数据库,备份所有ucenter数据表,也就是用户数据.其他DZ论坛搭建完成以后,可以上传用户数据库,将备份文件使用上传至网站所使用的主 ...

  4. DataRow 点不出 Select

    DataRow 点不出 Select?why?using system.linq;

  5. Spark核心RDD:combineByKey函数详解

    https://blog.csdn.net/jiangpeng59/article/details/52538254 为什么单独讲解combineByKey? 因为combineByKey是Spark ...

  6. uva 11183 Teen Girl Squad

    题意: 有一个女孩,需要打电话让所有的人知道一个消息,消息可以被每一个知道消息的人传递. 打电话的关系是单向的,每一次电话需要一定的花费. 求出打电话最少的花费或者判断不可能让所有人知道消息. 思路: ...

  7. qDeleteAll 之后必须清空容器

    [1]qDeleteAll应用示例 qDeleteAll源码如下: template <typename ForwardIterator> Q_OUTOFLINE_TEMPLATE voi ...

  8. Sqoop 学习之路

    sqoop 基础知识和基本操作可以参考这篇博客:https://www.cnblogs.com/qingyunzong/p/8807252.html#_label3

  9. Linux基础命令---文本统计wc

    wc 统计文件的字节数.单词数.行数. 此命令的适用范围:RedHat.RHEL.Ubuntu.CentOS.SUSE.openSUSE.Fedora. 1.语法         wc [选项]  f ...

  10. Andriod post Api与返回值

    vs后台api控制器  post接收参数----HttpContext.Current.Request.Params["account"].ToString() 返回值为对象返回, ...