POJ 1222 Java: Extended lights out 枚举

来源:互联网 发布:matlab矩阵中的最小值 编辑:程序博客网 时间:2024/06/11 08:07

题目描述: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 casesfor(int c=0; c<cases;c++){puzzle = new int[6][8];press = new int[6][8];//添加一行两列,使开关操作统一//puzzle[][]和press[][]已初始化为0for(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;}}



0 0