題目連結:http://acm.pku.edu.cn/JudgeOnline/problem?id=1222
題目大意:給你一個5行6列的矩陣分別表示30個燈,矩陣map[i][j]=1表示燈亮著, =0表示燈沒亮。要求你輸出解決方案
press[][],press[i][j]=1表示按一下,=0表示不按。使得最後狀態為所有燈都熄滅。
首先找出其中的一些規律:(1)第2次按下同一個按鈕時,將抵消第1次按下時所產生的結果。因此,每個按鈕最多隻需要按下一次;
(2)各個按鈕被按下的順序對最終的結果沒有影響;
這道題是在《程式設計引導及線上實踐》上看到的,當然代碼也是參照上面的寫的。該題是屬於“枚舉”類的。
如果要枚舉所有的情況有2^30種,這是不可能的。所以還要尋找一些規律:
當第一行的press已經確定了,操作之後:
如果位置(1, j)上的燈是點亮的,則要按下位置(2, j)上按鈕,即press[2][j]一定取1;
如果位置(1, j)上的燈是熄滅的,則不能按位置(2, j)上按鈕,即press[2][j]一定取0。
這樣依據press 的第一、二行操作矩陣中的按鈕,才能保證第一行的燈全部熄滅。依此類推,可以確定press第三 四 五 行的值。
所以具體實現方法就是:先枚舉出press第一行的所有情況,總共有2^6=64種。再根據 上面的規律確定press第三 四 五 行的值。最後判斷第五行的燈是否全部熄滅,若全部熄滅則找到了答案。 代碼實現如下:
註:我寫的是枚舉的方法,當資料大的時候還是很耗時的,此題更好的方法是“高斯消元法”;但是我目前還沒有看懂這種方法:
http://blog.csdn.net/shiren_Bod/archive/2010/07/26/5766907.aspx
#include <stdio.h>int map[7][8], press[7][8];int Test(void){ int i, j; for (i=2; i<=5; i++)//更新press[][]的值 { for (j=1; j<=6; j++) { press[i][j] = (map[i-1][j]+press[i-1][j]+press[i-1][j-1]+press[i-1][j+1]+press[i-2][j])%2; } } for (j=1; j<=6; j++)//檢驗最後一行燈是否全部熄滅 { if ((press[5][j-1]+press[5][j]+press[5][j+1]+press[4][j])%2 != map[5][j]) return 0; } return 1;}void Enum(void) //枚舉第一行press的2^6中情況{ int i; for (i=1; i<=6; i++) press[1][i] = 0; while (!Test()) //枚舉的方法其實就是枚舉6位位元的所有情況 { press[1][1]++; i = 1; while (press[1][i] == 2) { press[1][i] = 0; i++; press[1][i]++; } }}int main(){ int T, t, i, j; scanf("%d", &T); for (t=1; t<=T; t++) { for (i=1; i<=5; i++) for (j=1; j<=6; j++) scanf("%d", &map[i][j]); Enum(); printf("PUZZLE #%d/n", t); for (i=1; i<=5; i++) { for (j=1; j<6; j++) printf("%d ", press[i][j]); printf("%d/n", press[i][6]); } }}