Poj_2965 The Pilots Brothers' refrigerator(状态压缩,bfs)

来源:互联网 发布:linux自动登录ssh脚本 编辑:程序博客网 时间:2024/05/18 02:38

题意:

有一个4*4的矩阵,01填充,通过翻转把所有的0变为1,但是翻转[i,j]的同时,要把同行同列的都翻转。

思路:

最开始的时候写了裸的BFS,太过暴力所以果断TLE。看了题解才知道是状态压缩,很高兴能遇到这样经典的状态压缩题目。虽然有巧妙的解法,但是从状态压缩中学到了更多的东西。

首先有几个点要先明白:

1. 每个点最多翻转一次;(翻转2n次相当于没翻转,翻转2n+1次相当于翻转1次)

2. 每个点翻转的顺序无所谓,组合起来的结果相同;

3. unsigned short(16 bits)可以存储一个情况;

4. 最多有2^16=65536种组合情况;

5. 状态的转换可以用位运算完成;

这样我们可以用一个unsigned short型的变量来存储状态,另外用一个结构体数组来存储状态的前驱和变换次数,两个数组都只需要开到65536,这样我们再进行BFS效率就会高很多了。

代码实现:

#include <iostream>#include <cstdio>#include <cstdlib>#include <cstring>#include <algorithm>using namespace std;const int MAX = 65536;struct Node{    bool exist;    int pre;    int change;    int times;};int pos;int num;Node que[MAX];int dx[16] = {1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4};int dy[16] = {1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4};unsigned short lis[MAX];unsigned short update_pos(unsigned short cur,int type);void check(unsigned short cur);int main(){    char tmp;    int cnt = 0;    unsigned short initial = 0;    for(int i = 0;i < 4; i++ ){        for( int j = 0; j < 4; j++ ){            scanf("%c",&tmp);            if( tmp == '-' ){                initial |= (1<<cnt);            }            cnt++;        }        getchar();    }    for( int i = 0; i < MAX; i++ ){        que[i].change = -1;        que[i].exist = false;        que[i].pre = -1;        que[i].times = 0;    }    que[initial].exist = true;    pos = 0;    num = 1;    lis[0] = initial;    //check(initial);    while( pos < num ){        unsigned short cur = lis[pos];        for( int i = 0; i < 16; i++ ){            unsigned short rep = update_pos(cur,i);            //check(rep);            if( que[rep].exist == true ){                continue;            }            que[rep].exist = true;            que[rep].pre = cur;            que[rep].change = i;            que[rep].times = que[cur].times+1;            lis[num] = rep;            num++;            //结果可以按任意顺序输出            if( rep == 0xffff ){                printf("%d\n",que[rep].times);                while( que[rep].pre != -1 ){                    printf("%d %d\n",dx[que[rep].change],dy[que[rep].change]);                    rep = que[rep].pre;                }                return 0;            }        }        pos++;    }    return 0;}unsigned short update_pos(unsigned short cur,int type){    unsigned short mod;    switch(type){        case 0:            mod = 0x111f;break;        case 1:            mod = 0x222f;break;        case 2:            mod = 0x444f;break;        case 3:            mod = 0x888f;break;        case 4:            mod = 0x11f1;break;        case 5:            mod = 0x22f2;break;        case 6:            mod = 0x44f4;break;        case 7:            mod = 0x88f8;break;        case 8:            mod = 0x1f11;break;        case 9:            mod = 0x2f22;break;        case 10:            mod = 0x4f44;break;        case 11:            mod = 0x8f88;break;        case 12:            mod = 0xf111;break;        case 13:            mod = 0xf222;break;        case 14:            mod = 0xf444;break;        case 15:            mod = 0xf888;break;    }    return cur^mod;}void check(unsigned short cur){    for( int i = 0; i < 4; i++ ){        for( int j = 0; j < 4; j++ ){            printf("%d ",cur%2);            cur /= 2;        }        printf("\n");    }    printf("\n");}


另外的一种巧妙解法,也很好理解:

每一个+都可以通过7次翻转变为-(中间加同行同列每个格子转一次),同时同行同列的其余六个格子(变换4次)不改变,其余格子(变换2次)也不改变。那么我们可以按照这个方法操作每一个关闭的开关'+',想要得到最少就是要去掉不必要的翻转,而不必要的翻转就是那些操作了偶数次的点(因为一个点翻转了偶数次的效果和不翻转效果相同)。之后把那些统计操作次数为奇数次的点按顺序操作一次就好了(因为一个点操作奇数次和操作1次的效果相同,且顺序任意)。

代码实现:

#include <iostream>#include <cstdio>#include <cstdlib>#include <cstring>#include <algorithm>using namespace std;int res;int cnt[16];int main(){    res = 0;    char tmp;    memset(cnt,0,sizeof(cnt));    for( int i = 0; i < 16; i++ ){        cin>>tmp;        if( tmp == '+' ){            int row = i/4;            int column = i%4;            cnt[row*4]++;            cnt[row*4+1]++;            cnt[row*4+2]++;            cnt[row*4+3]++;            cnt[column]++;            cnt[column+4]++;            cnt[column+8]++;            cnt[column+12]++;            cnt[row*4+column]--;        }    }    for( int i = 0; i < 16; i++ ){        if( cnt[i]%2 == 1 ){            res++;        }    }    printf("%d\n",res);    for( int i = 0; i < 16; i++ ){        if( cnt[i]%2 == 1 ){            printf("%d %d\n",i/4+1,i%4+1);        }    }    return 0;}



0 0
原创粉丝点击