POJ 1222 Java: Extended lights out 枚舉

題目描述:http://poj.org/problem?id=1222


本題使用到的算法是枚舉法。要求得使燈全部熄滅的按開關方式,最簡單的方法就是將30個開關的每一種狀態都枚舉一遍。但是這樣做需要進行2^30次計算,計算量太大。那麼是否可以減少枚舉的狀態的數量呢?

基本思路:嘗試尋找某一個“局部”,當這個“局部”的狀態確定下來了以後,其餘部分的情況必須根據這個“局部”來確定,從而只要唯一或不多的幾種可能。如此的話,我們只需要枚舉這個“局部”的每一種狀態,來判斷是否符合標準即可。

在本題中,我們可以將第一行,看成一個“局部”,原因如下:

1、通過題意我們知道,一個燈的狀態,只與其自身和相鄰的四個開關有關。那麼如果在第一行的開關狀態已經確定的情況下,想要改變第一行第i個燈的狀態,只有按第二行第i個開關,因此,第二行的狀態是唯一的。

2、同理,在第二行開關狀態確定了的情況下,第三行的開關狀態也是可以唯一確定的。一次類推,我們可以確定以下的每一行開關的狀態。

3、確定了每一行的開關狀態以後,我們還要來看看燈是否全都熄滅。由於之前開關確定的條件就是使得上一行的燈全部熄滅,因此我們只需要檢查最後一行燈是否被完全熄滅。

使用這種方法,我們只需要枚舉第一行的狀態,總共只有2^6種,大大減少了枚舉狀態數目。


接下來看一下算法的具體實現:

我們用兩個二維數組來表示,puzzle[][]表示燈的狀態,press[][]表示開關的狀態

1、在枚舉第一行的狀態時,我們可以用最簡單地使用6次for循環。這裏使用模擬二進制進位的方法來進行枚舉,代碼實現如下(guess()方法表示判斷此情況下燈是否完全熄滅):

//用二進制加法進位,模擬第一行開關的枚舉
		while(guess() == false){
			int c = 1;
			press[1][1]++;
			while(press[1][c] >1){
				press[1][c] = 0;
				c++;
				press[1][c]++;
			}
		}
2、確定計算開關狀態和判斷最後一行燈是否熄滅的公式,根據一個燈的狀態與五個開關的關係,我們可以列出關係式如下:

判斷開關(r+1, c)的狀態:

press[r+1][c] = (puzzle[r][c] + press[r][c] + press[r-1][c] + press[r][c+1] + press[r][c-1])%2
判斷燈(5,c)熄滅的條件:

puzzle[5][c] == (press[5][c] + press[5][c-1] + press[5][c+1] + press[4][c])%2

   但是我們會發現,處於邊界上的開關和燈的狀態,不符合這些公式,因此爲了方便起見,我們將puzzle和press加上一行兩列,加出來的值均爲0,既:

puzzle = new int[6][8];

press = new int [6][8];

3、用enumerate()方法枚舉第一行的每一種情況,用guess()方法來判斷在此枚舉狀態下燈能否完全熄滅。

全部AC代碼如下:

//poj 1222 熄燈問題
import java.util.Scanner;

public class Extended_lights_out {

	public static int[][] puzzle = null;
	public static int[][] press= null;
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int cases = sc.nextInt();//有幾個lights cases
		
		for(int c=0; c<cases;c++){	
			puzzle = new int[6][8];
			press = new int[6][8];//添加一行兩列,使開關操作統一
			//puzzle[][]和press[][]已初始化爲0
			for(int i = 1; i < 6 ;i++){
				for(int j = 1; j < 7; j++){
					puzzle[i][j] = sc.nextInt();
				}
			}			
			
			enumerate();
			System.out.println("PUZZLE #" + (c+1));
			int count = 0;
			for(int r=1; r<6; r++){
				for(int c1=1; c1<7; c1++){
					System.out.print(press[r][c1] + " ");
				}
				
				if(count < 5){
					System.out.println(" ");
					count++;
				}

			}
		}
		sc.close();
	}
	
	//對第一行進行枚舉
	public static void enumerate(){
		for(int i=1; i<7;i++){
			press[1][i] = 0;
		}
		//用二進制加法進位,模擬第一行開關的枚舉
		while(guess() == false){
			int c = 1;
			press[1][1]++;
			while(press[1][c] >1){
				press[1][c] = 0;
				c++;
				press[1][c]++;
			}
		}
	}
	
	//根據第一行的枚舉情況確定後面的開關狀態,並判斷能否實現全滅
	public static boolean guess(){
		for(int r = 1; r<5; r++){
			for(int c = 1; c<7; c++){
				press[r+1][c] = (puzzle[r][c] + press[r][c] + press[r-1][c] + press[r][c+1] + press[r][c-1])%2;
			}
		}
		//判斷第五行所有的燈是否都熄滅
		for(int c=1; c<7; c++){
			if(puzzle[5][c] != (press[5][c] + press[5][c-1] + press[5][c+1] + press[4][c])%2) return false;
		}
		return true;
	}
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章