http://poj.org/problem?id=1038
这题废了我n长时间
poj 1038 Bugs Integrated, Inc.三进制状压dp
先把图片旋转下M为横轴N为纵轴
发现题目要求放2x3的方块,所以至少要存三行的状态,考虑三进制状压。

讨论状态

对于(x,y)(x,y)每一个三进制为
0:(x,y),(x,y1)0:(x,y),(x,y-1)都没有被占用
1:(x,y)1:(x,y)没有占用,(x,y1)(x,y-1)被占用
3:(x,y)3:(x,y)被占用,(x,y1)(x,y-1)可以不考虑
占用指的是:有坏点或被其他芯片覆盖
通过讨论(x,y)(x,y)来判断(x,y+1)(x,y+1)是否可能成为一个芯片的左上角
状态初始化全部为-1,代表不可到达

设置转移

dp[i][j]dp[i][j]表示第ii行状态jj时,1i11…i-1行所能放芯片的最大数目
1、这一行的初始状态可以由下一行转移得来
下面(x,y1),(x,y),(x,y+1)(x,y-1),(x,y),(x,y+1)都表示这个坐标对应的状态
(x,y1)(x,y-1)为0,(x,y)(x,y)为0
(x,y1)(x,y-1)为1,(x,y)(x,y)为0
(x,y1)(x,y-1)为2,(x,y)(x,y)为1
特别的当(x,y)(x,y)为坏点,(x,y)(x,y)为2
2、开始dfs过程
参数xx表示所到达的列,ii代表当前的行
(i,x)(i,x)放\不放芯片,讨论的都是(i,x)(i,x)作为左上角位置
p,qp,q分别代表下一行和当前dfs的这一行

  1. (i,x)(i,x)不放芯片,dfs直接跳转到(i,x+1)(i,x+1)
  2. (i,x)(i,x)放竖着的芯片,前提p[x]=0,p[x+1]=0,q[x]=0,q[x+1]=0p[x]=0,p[x+1]=0,q[x]=0,q[x+1]=0,当前已经枚举的放置个数+1转移到dp[i+1][q]dp[i+1][q']qq'表示qq放置芯片后的状态q[x]=q[x+1]=2q[x]=q[x+1]=2,dfs跳转到(i,x+2)(i,x+2)
  3. (i,x)(i,x)放横着的芯片,前提xm2,q[x]=0,q[x+1]=0,q[x+2]=0x\le m-2,q[x]=0,q[x+1]=0,q[x+2]=0,当前已经枚举的放置个数+1转移到dp[i+1][q]dp[i+1][q']qq'表示qq放置芯片后的状态q[x]=q[x+1]=q[x+2]=2q[x]=q[x+1]=q[x+2]=2,dfs跳转到(i,x+3)(i,x+3)
    转移就结束了,详细的过程可以在代码中体现
#include <cstdio>
#include <cstring>
#include <iostream>
#define LL long long
using namespace std;
inline void read(int &x){
    x = 0; int f = 1; char ch = getchar();
    while (!(ch >= '0' && ch <= '9')){if (ch == '-') f = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9'){x = x * 10 + ch - '0'; ch = getchar();}
    x *= f;
}
inline void Max(int &x, int y){if (y > x) x = y;}
int TT, n, m, k, ans, hit[151][11], f[2][59049], thr[11], p[11], q[11];
bool fg;
inline int make(int *a){//三进制转十进制
    int ret = 0;
    for (int i = 0; i < m; i++) ret += a[i + 1] * thr[i];
    return ret;
}
inline void res(int num, int *a){//十进制转三进制
    for (int i = 1; i <= m; i++) a[i] = num % 3, num /= 3;
}
inline void dfs(int x, int cnt){//x表示枚举到的列,cnt表示已经放置芯片的个数
    Max(ans, cnt);//更新ans
    Max(f[fg][make(q)], cnt);//更新当前q的状态值
    for (; x < m; x++){//通过循环省去转移方案1,也可以改为dfs(x+1,cnt),但更慢
    	//转移方案2
        if (p[x] == 0 && p[x + 1] == 0 && q[x] == 0 && q[x + 1] == 0){
            q[x] = q[x + 1] = 2;
            dfs(x + 2, cnt + 1);
            q[x] = q[x + 1] = 0;
        }
    	//转移方案3
        //这个位置写成else if好像也能ac,不知为何
        if (x <= m - 2 && q[x] == 0 && q[x + 1] == 0 && q[x + 2] == 0){
            q[x] = q[x + 1] = q[x + 2] = 2;
            dfs(x + 3, cnt + 1);
            q[x] = q[x + 1] = q[x + 2] = 0;
        }
    }
}
int main(){
	//预处理3的m次幂
    thr[0] = 1;
    for (int i = 1; i <= 10; i++) thr[i] = thr[i - 1] * 3;
    read(TT);
    while (TT--){
    	//hit表示坏点坐标
        memset(hit, 0, sizeof(hit)), memset(f, -1, sizeof(f));
        //fg用于滚动数组
        fg = 0; ans = 0;
        read(n), read(m), read(k);
        for (int i = 1, x, y; i <= k; i++) read(x), read(y), hit[x][y] = true;
        //预处理第一行,坏点为2,好点为1
        for (int i = 1; i <= m; i++) if (hit[1][i]) p[i] = 2; else p[i] = 1;
        f[1][make(p)] = 0;
        for (int i = 2; i <= n; i++){
        	//记得初始化
            memset(f[fg], -1, sizeof(f[fg]));
            for (int j = 0; j < thr[m]; j++) if (f[fg ^ 1][j] != -1){
                res(j, p);
                for (int o = 1; o <= m; o++){
                	//状态初始化见上
                    if (hit[i][o]) q[o] = 2;
                    else q[o] = max(0, p[o] - 1);
                }
                dfs(1, f[fg ^ 1][j]);
            }
            fg ^= 1;
        }
        printf("%d\n", ans);
    }
    return 0;
}

从这位dalao学来的%%%
http://www.cnblogs.com/dengeven/p/3237382.html

相关文章: