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;
	}
}



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