熄燈問題(ACM)(列舉案例)

2021-08-09 15:04:59 字數 4131 閱讀 9069

問題描述

乙個由按鈕組成的矩陣,其中每行有6個按鈕,共5行。每個按鈕的位置上有一盞燈。當按下乙個按鈕後,該按鈕以及周圍位置(上邊、下邊、左邊、右邊)的燈都會改變一次。即,如果燈原來是點亮的,就會被熄滅;如果燈原來是熄滅的,則會被點亮。在矩陣角上的按鈕改變3盞燈的狀態;在矩陣邊上的按鈕改變4盞燈的狀態;其他的按鈕改變5盞燈的狀態。

在上圖中,左邊矩陣中用x標記的按鈕表示被按下,右邊的矩陣表示燈狀態的改變。對矩陣中的每盞燈設定乙個初始狀態。請你按按鈕,直至每一盞等都熄滅。與一盞燈毗鄰的多個按鈕被按下時,乙個操作會抵消另一次操作的結果。在下圖中,第2行第3、5列的按鈕都被按下,因此第2行、第4列的燈的狀態就不改變。

請你寫乙個程式,確定需要按下哪些按鈕,恰好使得所有的燈都熄滅。根據上面的規則,我們知道:

(1)第2次按下同乙個按鈕時,將抵消第1次按下時所產生的結果。因此,每個按鈕最多隻需要按下一次;

(2)各個按鈕被按下的順序對最終的結果沒有影響;

(3)對第1行中每盞點亮的燈,按下第2行對應的按鈕,就可以熄滅第1行的全部燈。如此重複下去,可以熄滅第1、2、3、4行的全部燈。同樣,按下第1、2、3、4、5列的按鈕,可以熄滅前5列的燈。

輸入資料

第一行是乙個正整數n,表示需要解決的案例數。每個案例由5行組成,每一行包括6個數字。這些數字以空格隔開,可以是0或1。0表示燈的初始狀態是熄滅的,1表示燈的初始狀態是點亮的。

輸出要求

對每個案例,首先輸出一行,輸出字串「puzzle #m」,其中m是該案例的序號。接著按照該案例的輸入格式輸出5行,其中的1表示需要把對應的按鈕按下,0則表示不需要按對應的按鈕。每個數字以乙個空格隔開。

[plain] view plain copy

輸入樣例

2 0 1 1 0 1 0

1 0 0 1 1 1

0 0 1 0 0 1

1 0 0 1 0 1

0 1 1 1 0 0

0 0 1 0 1 0

1 0 1 0 1 1

0 0 1 0 1 1

1 0 1 1 0 0

0 1 0 1 0 0

[plain] view plain copy

輸出樣例

puzzle #1

1 0 1 0 0 1

1 1 0 1 0 1

0 0 1 0 1 1

1 0 0 1 0 0

0 1 0 0 0 0

puzzle #2

1 0 0 1 1 1

1 1 0 0 0 0

0 0 0 1 0 0

1 1 0 1 0 1

1 0 1 1 0 1

****

問題分析圖

void setvalue(int s,int j,char &c)//設定某乙個位置的值;

void getoppose(char &c,int j)//將某乙個位置的值,取反;

cout

;//原始的熄滅狀態

char ligths[5] = ;//變化中的燈的熄滅狀態

char result[5] = ;//最後的結果,,開關的狀態;

char flag ;//每行開關的狀態;;

int caseno = 0;

cin>>caseno;

for(int t = 1;t<=caseno;t++)

}//輸入操作;

for( int n = 0;n<64;n++)

}//第i行的開關狀態和熄滅狀態確定;

if(i<4)

flag = ligths[i]; //下一行的開關狀態也確定了;

}// 判斷最後一行是不是都為0;是就是列印result的值;不是就繼續下乙個假設;

if(ligths[4]==0)}}

return

0;}為了敘述方便,為按鈕矩陣中的每個位置分別指定乙個座標。用陣列元素 puzzle[i][j] 表示位置 (i, j) 上燈的初始狀態:1 表示燈是被點亮的;0 表示燈是熄滅的。用陣列元素 press[i] [j] 表示為了讓全部的燈都熄滅,是否要按下位置 (i, j) 上的按鈕:1 表示要按下;0 表示不用按下。

由於第 0 行、第 0 列和第 7 列不屬於按鈕矩陣的範圍,沒有按鈕,可以假設這些位置上的燈總是熄滅的,按鈕也不用按下。其它 30 個位置上的按鈕是否需要按下是未知的。因此陣列 press 共有 230種取值。從這麼大的乙個空間中直接搜尋我們要找的答案,顯然代價太大、不合適。

我們從熄燈的規則中,發現答案中的元素值之間的規律。不滿足這個規律的陣列 press,就沒有必要進行判斷了。根據熄燈規則,如果矩陣 press 是尋找的答案,那麼按照 press 的第一行對矩陣中的按鈕操作之後,此時在矩陣的第一行上:

如果位置 (1, j) 上的燈是點亮的,則要按下位置 (2, j) 上按鈕,即 press[2] [j] 一定取 1;

如果位置 (1, j) 上的燈是熄滅的,則不能按位置 (2, j) 上按鈕,即 press[2] [j] 一定取 0。

因為燈的最後狀態(是否按它下一行開關之前)與周圍燈是否按按鈕有關,如 puzzle[i] [j],決定它最後狀態的相關按鈕為 press[i] [j-1] (左),press[i][j](它本身),press[i] [j+1](右),press[i-1][j](上),還有它最初的狀態 puzzle[i] [j]。考慮到按兩次按鈕作用會抵消,需要取它們的和與2的餘數,所以puzzle[i] [j] 最後狀態公式為:

puzzle[i][j]最後狀態 = (press[i][j-1]+press[i][j]+press[i][j+1]+press[i-1][j]+ puzzle[i] [j] ) % 2

而press[i+1] [j] 由 puzzle[i] [j] 的最後狀態決定,燈亮著(值為1)就要關掉它,press值取1,燈是熄滅的(值為0)不用處理,press值取0,它們是相等的,所以press[i+1] [j] = puzzle[i] [j] 最後狀態。

這樣依據 press 的第

一、二行操作矩陣中的按鈕,才能保證第一行的燈全部熄滅。而對矩陣中第

三、四、五行的按鈕無論進行什麼樣的操作,都不影響第一行各燈的狀態。依此類推,可以確定 press 第

三、四、五行的值。因此,一旦確定了 press 第一行的值之後,為熄滅矩陣中第一至四行的燈,其他行的值也就隨之確定了。

press 的第一行共有 64(2^7)種取值,分別對應唯一的一種 press 取值,使得矩陣中前四行的燈都能熄滅。只要對這 64種情況進行判斷就可以了:如果按照其中的某個 press對矩陣中的按鈕進行操作後,第五行的所有燈也恰好熄滅,則找到了答案。

我們這樣來判斷第五行燈是否是熄滅的,如puzzle[5] [j], 它的最終狀態由按鈕press[5] [j-1] (左),press[5][j](它本身),press[5] [j+1](右),press[4] [j](上)決定,如果puzzle[5] [j]原先亮著(值為1),那麼決定它最終狀態的按鈕作用互相抵消後,某乙個按鈕仍然需要是按下的(值為1),如果puzzle[5] [j]原先熄滅的(值為0),那麼決定它最終狀態的按鈕作用互相抵消後,沒有按鈕是按下的(值為0)。判斷公式為:

if ( puzzle [5] [j] == ( press [5] [j-1] + press [5] [j] + press [5] [j+1] + press [4] [j] )

如果是相等的,則該燈是熄滅的,否則它還是亮著的。

c++**

#include

int press[6][8];

int puzzle[6][8];

bool guess()

}for(j=1;j<=6;j++)

return

true;

}void process()

}}int main()

printf("\n");

}return

0;}

列舉 熄燈問題

有乙個由按鈕組成的矩陣,5行6列,每按一次改變原來顏色 具體事例如下圖所示 請寫乙個程式,判斷需要按哪些按鈕,能夠是燈泡全部熄滅。相關問題細節不再重複 剛剛輸入 2 0 1 1 0 1 0 1 0 0 1 1 1 0 0 1 0 0 1 1 0 0 1 0 1 0 1 1 1 0 0 0 0 1 0...

熄燈問題 列舉

1 問題描述 有乙個由按鈕組成的矩陣,其中每行有6個按鈕,共 5行。每個按鈕的位置上有一盞燈。當按下乙個按鈕後,該按鈕以及周圍位置 上邊 下邊 左邊 右邊 的燈都會改變一次。即,如果燈原來是點亮的,就會被熄滅 如果燈原來是熄滅的,則會被點亮。在矩陣角上的按鈕改變 3盞燈的狀態 在矩陣邊上的按鈕改變 ...

列舉 熄燈問題

問題描述 有乙個由按鈕組成的矩陣,其中每行有6個按鈕,共5行。每個按鈕的位置上有一盞燈。當按下乙個按鈕後,該按鈕以及周圍位置 上邊 下邊 左邊 右邊 的燈都會改變一次。即,如果燈原來是點亮的,就會被熄滅 如果燈原來是熄滅的,則會被點亮。在矩陣角上的按鈕改變3盞燈的狀態 在矩陣邊上的按鈕改變4盞燈的狀...