tyvj P1266 費解的開關
阿新 • • 發佈:2018-12-23
題目:
描述
你玩過“拉燈”遊戲嗎?25盞燈排成一個5x5的方形。每一個燈都有一個開關,遊戲者可以改變它的狀態。每一步,遊戲者可以改變某一個燈的狀態。遊戲者改變一個燈的狀態會產生連鎖反應:和這個燈上下左右相鄰的燈也要相應地改變其狀態。我們用數字“1”表示一盞開著的燈,用數字“0”表示關著的燈。下面這種狀態
10111
01101
10111
10000
11011
在改變了最左上角的燈的狀態後將變成:
01111
11101
10111
10000
11011
再改變它正中間的燈後狀態將變成:
01111
11001
11001
10100
11011
給定一些遊戲的初始狀態,編寫程式判斷遊戲者是否可能在6步以內使所有的燈都變亮。
輸入格式
以下若干行資料分為n組,每組資料有5行,每行5個字元。每組資料描述了一個遊戲的初始狀態。各組資料間用一個空行分隔。
對於30%的資料,n<=5;
對於100%的資料,n<=500。
輸出格式
輸出資料一共有n行,每行有一個小於等於6的整數,它表示對於輸入資料中對應的遊戲狀態最少需要幾步才能使所有燈變亮。對於某一個遊戲初始狀態,若6步以內無法使所有燈變亮,請輸出“-1”。
測試樣例1
輸入
3
00111
01011
10001
11010
11100
11101
11101
11110
11111
11111
01111
11111
11111
11111
11111
輸出
3
2
思路:
二進位制儲存25盞燈的開關情況,再BFS列舉所由變的方法。
為了節省效率,不用在300組測試樣例中每組都BFS,可以再開頭就算好6步以內可以由全部都是開的變成的所有情況,把步數記下,輸入後就直接查詢即可。
在進行開關時,用位運算把第k位改變的操作為 x^(1<<(k-1)) ,要注意邊界的判斷,改變左右的燈時,要判斷的是是否超出了這一行的邊界,而不僅僅是判斷是否超出了25和0。
程式碼:
#include <cstdio> #include <iostream> #include <queue> #include <map> using namespace std; struct Light { int x; int num; Light(int a,int b) { x=a; num=b; } }; int Change(int x,int y) { x=x^(1<<(y-1)); if(y+5<=25) { x=x^(1<<(y+4)); } if(y-5>0) { x=x^(1<<(y-6)); } if(y%5!=0) { x=x^(1<<(y)); } if(y%5!=1) { x=x^(1<<(y-2)); } return x; } map<int,int> use; int bfs(int x) { queue<Light> que; que.push(Light(x,0)); use[x]=0; while(!que.empty()) { Light head=que.front(); que.pop(); if(head.num>=6) { return -1; } for(int i=1; i<=25; i++) { int now=Change(head.x,i); if(!use.count(now)) { use[now]=head.num+1; que.push(Light(now,head.num+1)); } } } return -1; } int main() { bfs(33554431); int t; scanf("%d",&t); while(t--) { int a[30]= {0}; for(int i=0; i<5; i++) { string y; cin>>y; for(int j=0; j<5; j++) { a[i*5+j]=y[j]-'0'; } } int x=0; int z=1; for(int i=24; i>=0; i--) { x+=z*a[i]; z*=2; } if(use.count(x)) printf("%d\n",use[x]); else printf("-1\n"); } return 0; }