poj 1038 Bugs Integrated, Inc.三进制状压dp

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

讨论状态

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

设置转移

d p [ i ] [ j ] dp[i][j] 表示第 i i 行状态 j j 时, 1 i 1 1…i-1 行所能放芯片的最大数目
1、这一行的初始状态可以由下一行转移得来
下面 ( x , y 1 ) , ( x , y ) , ( x , y + 1 ) (x,y-1),(x,y),(x,y+1) 都表示这个坐标对应的状态
( x , y 1 ) (x,y-1) 为0, ( x , y ) (x,y) 为0
( x , y 1 ) (x,y-1) 为1, ( x , y ) (x,y) 为0
( x , y 1 ) (x,y-1) 为2, ( x , y ) (x,y) 为1
特别的当 ( x , y ) (x,y) 为坏点, ( x , y ) (x,y) 为2
2、开始dfs过程
参数 x x 表示所到达的列, i i 代表当前的行
( i , x ) (i,x) 放\不放芯片,讨论的都是 ( i , x ) (i,x) 作为左上角位置
p , q p,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 ] = 0 p[x]=0,p[x+1]=0,q[x]=0,q[x+1]=0 ,当前已经枚举的放置个数+1转移到 d p [ i + 1 ] [ q ] dp[i+1][q'] q q' 表示 q q 放置芯片后的状态 q [ x ] = q [ x + 1 ] = 2 q[x]=q[x+1]=2 ,dfs跳转到 ( i , x + 2 ) (i,x+2)
  3. ( i , x ) (i,x) 放横着的芯片,前提 x m 2 , q [ x ] = 0 , q [ x + 1 ] = 0 , q [ x + 2 ] = 0 x\le m-2,q[x]=0,q[x+1]=0,q[x+2]=0 ,当前已经枚举的放置个数+1转移到 d p [ i + 1 ] [ q ] dp[i+1][q'] q q' 表示 q q 放置芯片后的状态 q [ x ] = q [ x + 1 ] = q [ x + 2 ] = 2 q[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

猜你喜欢

转载自blog.csdn.net/weixin_44627639/article/details/88404112