nkoj 2223
Description
有一个叫“飞行员兄弟的冰箱”的著名智力游戏,在游戏中玩家需要打开一个冰箱。
在冰箱的门上有16个门把手,每个门把手只有两种状态:打开和关闭。当所有门把手都处于打开状态时,冰箱才会被打开。
16个门把手构成了一个4x4的矩阵,当你改变坐标为(x,y)的门把手的状态时,跟它同处于x行和跟它同处于y列的门把手们的状态都同时会被改变(开的变为关,关的变为开)。
问最少转动几次把手就可以打开冰箱门?
Input
输入一个有"+"和"-"构成的4x4的矩阵,"+"表示该把手处于关闭状态,"-"表示该把手处于打开状态。
Output
一个整数,表示最小步数
如果无解,输出"NO"
Sample Input
-+--
----
----
-+--
Sample Output
6
分析:基本思路当然是BFS但是存在以下问题:
1.怎样存储状态?
2.怎样判定是否达到目标或者该状态已经搜索过?
利用位运算,每一个二进制位代表一个开关。那么所有状态都对应着一个0~2^16-1之间的一个整数。
vis[i]表示该状态是否被搜索过
代码如下:
#include<iostream> #include<cstdio> #include<queue> using namespace std; int start=0; bool vis[66666]; struct node{ int s,step; node (int a,int b){ this -> s = a ; this -> step = b ; } }; queue <node> q; int convert(int cur,int x){ int i,y = x; while(y%4!=0)y--; for(i=0;i<4;i++) //同一行 cur ^= (1<<(y+i)); y=x; while(x>=4)x-=4; //同一列 for(;x<16;x+=4) cur ^= (1<<(x)); cur ^= (1<<(y)); //自身被行、列各异或了一次,没有改变 return cur; } void input(){ int i=0; while(i<16){ char ch=getchar(); if(ch=='+'||ch=='-'){ if(ch=='+')start |= (1 << i) ; i++ ; } } } bool BFS(){ q.push(node (start,0)); vis[start]=true; if(!start){ cout<<"0"; return true; } while(!q.empty()){ node t=q.front(); q.pop(); for(int i=0;i<16;i++){ //改变每一位 int cur=convert(t.s,i); if(!cur){ //找到解 cout<<t.step+1; return true; } if(!vis[cur]){ // 没有被搜索过 q.push(node (cur,t.step+1)); vis[cur]=true; } } } return false; } int main(){ input(); if(!BFS()) cout<<"No"; return 0; }