HDU 4949 Light(插头dp、位运算)

比赛的时候没看题,赛后看题觉得比赛看到应该可以敲的,敲了之后发现还真就会卡题。。

因为写完之后,无限TLE。。。

直到后来用位运算代替了我插头dp常用的decode、encode、shift三个函数以及改改HASH值才勉强过的。。。7703ms

题意:给一个N*M的01矩阵,每次可以选一个格子进行2种操作,①翻转邻居格子②翻转邻居格子和自己。输出最小的总操作数使得矩阵全为0.

显然每个格子有4种操作(一、不操作;二、①②;三、①;四、②)。

一开始写的时候用2位表示一个插头,一位用于表示翻转当前格子,一位表示插头的源头需要被翻转。然后空间就是2*3*(4^10)感觉有点不科学

后来发现,其实,我们可以这样归类,①不操作(花费0);②翻自己(花费2);③翻转邻居(花费1);这样空间就是2*3*(3^10)

其中③包括2种情况,事实上,如果对一个格子A进行了第③种操作,那这个格子的邻居格子BCDE做任何操作,A都可以熄灯

还有就是,答案一定小于等于一开始矩阵的1的个数的2倍,可以用这个进行一定程度的剪枝。

然后就是各种位运算了。。。。搞得我都晕了。。。

另外,其实,因为这样插头dp需要消耗很多额外的花费(清空hash表什么的),所以速度应该是比直接dp[i][j][k]要慢一些的(主要应该是m小的时候k<tot清空比清空hash表快)。

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;


#define HASH 100007
#define STATE 500010
#define maxd 15

int maze[maxd][maxd];
int code[maxd];
int n,m;
struct HASHMAP{
    int head[HASH];
    int state[STATE],nxt[STATE];
    int f[STATE];
    int sz;
    void clear(){sz=0;memset(head,-1,sizeof(head));}
    void push(int st,int ans){
        int h=st%HASH;
        for(int i=head[h];i!=-1;i=nxt[i]){
            if(st==state[i]){
                f[i] = f[i]<ans?f[i]:ans;
                return ;
            }
        }
        state[sz]=st,nxt[sz]=head[h],f[sz]=ans;
        head[h]=sz++;
    }
}hm[2];
void decode(int st){
    for(int i=m;i>=0;--i) code[i]=st&3,st>>=2;
}
int encode(){
    int ret=0;
    for(int i=0;i<=m;++i) ret=ret<<2|code[i];
    return ret;
}
void shift(){
    for(int i=m;i;--i) code[i]=code[i-1];
    code[0]=0;
}
int ans;
int zo,oz,oo;
void dpblank(int i,int j,int cur){
    int mv = j==m?2:0;
    int all = (1<<(2*(m+1)-mv) ) -1;
    for(int k=0;k<hm[cur].sz;++k){
        int st = hm[cur].state[k];
        int left = st&(oo>>(2*(j-1))), up = st&(oo>>(2*j));
        int L = left>>(2*(m-j+1)), U = up>>(2*(m-j));
        int cnt = ((L>>1)+(U>>1))&1;
        if(i==1 || U==2 || maze[i-1][j]==U){
            int st2 = st^left^up;
            if(cnt) st2 = st2 | (zo>>(2*(j-1))) | (zo>>(2*j));
            hm[cur^1].push((st2>>mv)&all, hm[cur].f[k]);
        }
        if(hm[cur].f[k]+2<ans)
        if(i==1 || U==2 || maze[i-1][j]==U){
            int st2 = st^left^up;
            if(!cnt) st2 = st2 | (zo>>(2*(j-1))) | (zo>>(2*j));
            hm[cur^1].push((st2>>mv)&all, hm[cur].f[k]+2);
        }
        if(hm[cur].f[k]+1<ans)
        if(i==1 || U==2 || maze[i-1][j]!=U){
            int st2 = st^left^up;
            if(j>1 && L!=2) st2 = st2 ^ (zo>>(2*(j-2)));
            st2 = st2 | (oz>>(2*(j-1))) | (oz>>(2*j));
            hm[cur^1].push((st2>>mv)&all, hm[cur].f[k]+1);
        }
    }
}
void solve(){
    zo = 1<<(2*m);
    oz = 2<<(2*m);
    oo = 3<<(2*m);
    int cur=0;
    hm[0].clear();
    hm[0].push(0,0);
    for(int i=1;i<=n;++i){
        for(int j=1;j<=m;++j){
            hm[cur^1].clear();
            dpblank(i,j,cur);
            cur^=1;
        }
    }
    for(int k=0;k<hm[cur].sz;++k){
        bool yes=true;
        decode(hm[cur].state[k]);
        for(int j=1;j<=m;++j){
            if(code[j]!=2 && code[j]!=maze[n][j]){
                yes=false;
                break;
            }
        }
        if(yes) ans = ans<hm[cur].f[k]?ans:hm[cur].f[k];
    }
}
int main(){
    int ca=0;
    while(~scanf("%d%d",&n,&m) && n){
        printf("Case #%d: ",++ca);
        ans=0;
        for(int i=1;i<=n;++i) for(int j=1;j<=m;++j)
            scanf("%1d",maze[i]+j), ans+=maze[i][j];
        ans*=2;
        solve();
        printf("%d
",ans);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/nextbin/p/3915042.html