1. 程式人生 > >熄燈問題 二進位制列舉

熄燈問題 二進位制列舉

0080:熄燈問題

總時間限制: 

1000ms

記憶體限制: 

65536kB

描述

有一個由按鈕組成的矩陣,其中每行有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列的燈。  

輸入

5行組成,每一行包括6個數字(0或1)。相鄰兩個數字之間用單個空格隔開。0表示燈的初始狀態是熄滅的,1表示燈的初始狀態是點亮的。

輸出

5行組成,每一行包括6個數字(0或1)。相鄰兩個數字之間用單個空格隔開。其中的1表示需要把對應的按鈕按下,0則表示不需要按對應的按鈕。

樣例輸入

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

樣例輸出

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

來源

1222

分析:

我們從上面可以得到大致思路:

當第一行的按鈕狀態被確定後,要將第一行還未熄滅的燈熄滅,則第二行也確定,第二行確定後,要講第二行還未熄滅的燈熄滅,則第三行的解決方案也唯一……如此推下去,則最後一行也確定了,這時判斷最後一行是否已經燈全熄滅,若全熄滅,則這就是正確的解決方案,如果沒有全熄滅,則改變第一行的按鈕狀態,迴圈判斷。 

我們需要解決的問題:

1.列舉第一行的所有狀態。

這裡介紹一直二進位制列舉:點這裡,程式碼很短

2.怎麼根據上一行的按鈕狀態以及燈的狀態來確定下一行的按鈕狀態

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

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

先給出程式碼:

a[i+1][j] = (maps[i][j] + a[i][j] + a[i][j-1] + a[i][j+1] + a[i-1][j]) % 2;

因為本行要設定的按鈕狀態只與此按鈕頭頂上那個燈當前的狀態有關,而頭頂上那個燈當前的狀態與其自己燈本身的狀態以及其對應按鈕的狀態及其上、左、右三個按鈕的狀態有關。因此得到通過這五種狀態的和餘2得到其當前狀態,如果頭頂上那個燈當前狀態為0,則我們不需要按下按鈕,按鈕狀態也為0,如果頭頂上那個燈是亮著的,狀態為1,則我們需要按下按鈕,按鈕狀態也為1。

3.開一個較大的陣列,就不要管越界了

import java.util.Arrays;
import java.util.Scanner;
public class Main
{	

	static Scanner cin=new Scanner(System.in); 
	static int [][] maps=new int [10][10];
	
	static int solve(int a[][])
	{
	    for(int i = 1; i <= 4; i ++)
	        for(int j = 1; j <= 6; j ++)
			a[i+1][j] = (maps[i][j] + a[i][j] + a[i][j-1] + a[i][j+1] + a[i-1][j]) % 2;
	    
	    for(int j = 1; j <= 6; j ++)  //最後一行
	        if((maps[5][j] + a[5][j] + a[5][j-1] + a[5][j+1] + a[5-1][j])%2!=0)
	            return 0;

	    return 1;
	}
	
	public static void main(String args[])
	{
		for(int i = 1; i <= 5; i ++)
	        for(int j = 1; j <= 6; j ++)
	           maps[i][j]=cin.nextInt();
		
	    for(int i = 0; i < (1<<6); i ++) //列舉第一行所有狀態
	    {
	        int [][] a=new int [10][10];
	        for(int j = 0; j < 6; j ++) //第一行的每一位,倒著列舉的
	        {
	            if((i&(1<<j))!=0) // 如果是 1;
	                a[1][6-j] = 1;
	        }
	        if(solve(a)!=0)
	           {
	        	for(int i1 = 1; i1 <= 5; i1 ++)
	    	    {
	    	        for(int j = 1; j <= 6; j ++)
	    	            System.out.print(a[i1][j]+" ");
	    	            System.out.println();
	    	    }
	        	break;
	           }
	    }
	}
}