這題與1753點燈遊戲很類似,只不過變換狀態的方式不一樣罷了..
狀態壓縮+BFS就可以解決,與1753不同的就是要記錄路徑,可以通過給隊列中的元素加上pre和next屬性來實現.
已耗用時間690MS,感覺挺慢的,後來在dicuss裡看到了一位大牛的總結,很好的方法啊..有時候做題前多思考真的是很重要啊,即使是對一些很面熟的題目.
“先看一個簡單的問題,如何把'+'變成'-'而不改變其他位置上的狀態?答案是將該位置(i,j)及位置所在的行(i)和列(j)上所有的handle更新一次,結果該位置被更新了7次,相應行(i)和列(j)的handle被更新了6次,剩下的被更新了4次.被更新偶數次的handle不會造成最終狀態的改變.因此得出高效解法,在每次輸入碰到'+'的時候,自增該位置與相應的行和列,當輸入結束後,遍曆數組,所有為奇數的位置則是操作的位置,而奇數位置的個數之和則是最終的操作次數.”,
#include <cstdio>#include <string.h>using namespace std;struct record{int va,pre,next,c;//分別記錄目前狀態,前驅,後繼,和操作 }r[70000]; int vi[70000];//是否訪問 int st;//起始狀態 int f(int x,int i){//位元運算更改狀態 x^=1<<i;//更改當前格 int lx=i/4,ly=i%4; //擷取行,列 for(int j=0;j<4;j++){x^=1<<(4*j+ly);//更改列 x^=1<<(4*lx+j);//更改行 }return x;}void solve(int pos){int a,b,res=1;int head=pos;while(r[head].pre!=0){//根據前驅向前找直到起始點, 記錄變換次數和後驅 r[r[head].pre].next=head;head=r[head].pre;res++;}printf("%d\n",res);for(int i=0;i<res;i++){//根據後驅和變換次數進行輸出printf("%d %d\n",r[head].c/4+1,r[head].c%4+1);head=r[head].next;}}void bfs(int x){if(x==0)return;int front=0,tail=0;r[0].va=x;vi[x]=1;tail++;while(1){int v=r[front].va;for(int i=0;i<16;i++){int ns=f(v,i);if(vi[ns]==0){r[tail].va=ns;r[tail].pre=front;r[tail].c=i;if(ns==0){//找到結果 solve(tail);return; }vi[ns]=1;tail++;}}front++;}}int main(){char line[6];st=0;for(int i=0;i<4;i++){scanf("%s",line);for(int j=0;j<4;j++){if(line[j]=='+')st+=1<<(4*i+j);}}memset(vi,0,sizeof vi);bfs(st);return 0;}