POJ1222-解题报告

问题描述:

EXTENDED LIGHTS OUT
Time Limit: 1000MS   Memory Limit: 10000K
Total Submissions: 12707   Accepted: 8059

Description

In an extended version of the game Lights Out, is a puzzle with 5 rows of 6 buttons each (the actual puzzle has 5 rows of 5 buttons each). Each button has a light. When a button is pressed, that button and each of its (up to four) neighbors above, below, right and left, has the state of its light reversed. (If on, the light is turned off; if off, the light is turned on.) Buttons in the corners change the state of 3 buttons; buttons on an edge change the state of 4 buttons and other buttons change the state of 5. For example, if the buttons marked X on the left below were to be pressed,the display would change to the image on the right. 

The aim of the game is, starting from any initial set of lights on in the display, to press buttons to get the display to a state where all lights are off. When adjacent buttons are pressed, the action of one button can undo the effect of another. For instance, in the display below, pressing buttons marked X in the left display results in the right display.Note that the buttons in row 2 column 3 and row 2 column 5 both change the state of the button in row 2 column 4,so that, in the end, its state is unchanged. 

Note: 
1. It does not matter what order the buttons are pressed. 
2. If a button is pressed a second time, it exactly cancels the effect of the first press, so no button ever need be pressed more than once. 
3. As illustrated in the second diagram, all the lights in the first row may be turned off, by pressing the corresponding buttons in the second row. By repeating this process in each row, all the lights in the first 
four rows may be turned out. Similarly, by pressing buttons in columns 2, 3 ?, all lights in the first 5 columns may be turned off. 
Write a program to solve the puzzle.

Input

The first line of the input is a positive integer n which is the number of puzzles that follow. Each puzzle will be five lines, each of which has six 0 or 1 separated by one or more spaces. A 0 indicates that the light is off, while a 1 indicates that the light is on initially.

Output

For each puzzle, the output consists of a line with the string: "PUZZLE #m", where m is the index of the puzzle in the input file. Following that line, is a puzzle-like display (in the same format as the input) . In this case, 1's indicate buttons that must be pressed to solve the puzzle, while 0 indicate buttons, which are not pressed. There should be exactly one space between each 0 or 1 in the output puzzle-like display.

Sample Input

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

Sample Output

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

Source



解题思路:

        这个问题是给出了一个5行6列的灯泡组合,1表示灯亮,0表示灯灭,当你按下开关的时候,开关所对应的灯泡将会改变状态(亮到灭或者灭到暗),同时,开关上下左右的灯泡也会一起改变状态,现在给出一个灯泡的状态阵列,要求你给出一个开关方案,能够将整个灯泡阵列全部变暗。并输出这个开关方案。

        我们可以很自然的想到,采用枚举的方式,试探每一种可能的开关方案,找到那个使全部开关都暗下来的方案并将其输出,但我们稍加思索就会发现,一共有30个灯泡,那么进行枚举就会有2^30种方案,这样的枚举我们显然是不能接受的,于是问题转化到如何减少枚举的次数。

        减少枚举次数的关键是,我们是否能找到一些状态,我们不需要对他进行试探也知道他必然是不正确的,对于开关来说,你开一次再关上一次是效果相同的,等效于没有操作,所以每个开关至多只要按一次。其次,我们可以仔细思考,既然灯只需按一次,那么先前的灯按了之后就无需再改动,我们可以选择先确定一个部分,这个部分确定之后就会自然的确定下一个部分。我们可以观察到,无论第一行灯的状态如何,我们总可以通过按第二行的开关来使第一行全部熄灭,然后第二行第三行第四行第五行都是如此,都可以通过按下一行的灯来让他们熄灭,到了第五行,为了让第四行熄灭,第五行的按法是唯一的,那么看起来第五行是“自身难保”的状态。真的是这样吗?其实不然,为了保证第四行的熄灭,第五行的状态确实无法由他自己控制了,但其实他是由第一行确定的,第一行的某一种开关状态,再经过第二行对第一行的关灯操作,再依次传递到第五行,会“恰好”使得第五行也是全灭的。那么我们怎么找到这个“恰好”呢?答案是显而易见的,枚举。

        先前的分析,实际上只有一个目的,就是减少枚举的次数,那么我们减少了吗?确实,从上面的分析可以看到,我们只需要枚举第一行的所有状态,就可以试探出所有可能正确的情况了,我们也就从2^30显著的减少到了2^6次枚举,到了一个可以接受的时间复杂范围。



源代码:

//枚举K个bit的01组合,只需要设置一个int数,从0~到2的K次方-1,一一枚举 
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<memory>
using namespace std; 
char oriLights[5];
char Lights[5];
char Result[5];
int GetBit(char c,int i);
void SetBit(char &c,int i,int v);
void FlipBit(char &c,int i);
void OutPutResult(int t,char result[]);

int main()
{
	int T;
	cin>>T;
	for(int t=1;t<=T;t++)
	{
		memset(oriLights,0,sizeof(oriLights));//将用来保存输入数据的数据清0
		for(int i=0;i<5;i++)
		{
			for(int j=0;j<6;j++)
		 	{
		 		int s;
		 		cin>>s;
		 		SetBit(oriLights[i],j,s);
		 	}
		}
		 for(int n=0;n<64;n++)
		 {
		 	char switchs=n;
		 	memcpy(Lights,oriLights,sizeof(oriLights));//由于接下来要对灯的状态进行修改,故我们先保存ori数据,复制到一个新数组中进行操作
		 	for(int i=0;i<5;i++)
		 	{
		 		Result[i]=switchs;
		 		for(int j=0;j<6;j++)
		 		{
		 			if(GetBit(switchs,j))
		 			{
		 				if(j>0)
		 				FlipBit(Lights[i],j-1);
		 				FlipBit(Lights[i],j);
		 				if(j<5)
		 				FlipBit(Lights[i],j+1);
					}
				}
				if(i<4)
				Lights[i+1]^=switchs;//对下一行灯进行操作 ,switchs恰好代表第i行灯的状态
			switchs=Lights[i];//程序走到这里,第i行的灯的情况,恰好就是i+1行开关的情况,因为所有亮着的灯都需要关掉,我们把开关状态更新至i+1 
			}
			if(Lights[4]==0)
			{
				OutPutResult(t,Result);
				break;
			}
		 }
	}
	return 0;
} 
 
int GetBit(char c,int i)//取字符c的第i个Bit 
{
	return(c>>i)&1;
}

void SetBit(char &c,int i,int v)
{
	if(v)
	c |=(1<<i);
	else
	c &=~(1<<i);
}

void FlipBit(char &c,int i)
{
	c^=(1<<i);
}

void OutPutResult(int t,char result[])
{
	cout<<"PUZZLE #"<<t<<endl;
	for (int i=0;i<5;i++)
	{
		for (int j=0;j<6;j++)
	 	{
	 		cout<<GetBit(result[i],j);
	 		if(j<5)
	 		cout<<" ";
	 	}
	 	cout<<endl;
	}
}

我从中学到了什么?

1.减少枚举次数的思路,通过局部的确定进而确定整体的结果

2.C++的位运算,这样一个5行6列的灯泡序列,我们可以用一个二维数组来保存,但其实每个灯泡只有0和1两种状态,恰好对应一个bit位,我们可以用一个Bit位来存储一个灯泡的状态,这样就可以大大节省空间上的开销。然后如何精确的对每一位数据进行操作呢?我们采用了移位,&,OR,XOR等方式进行,举例如下:

这是取Bit:

int GetBit(char c,int i)//取字符c的第i个Bit 
{
	return(c>>i)&1;
}

设置一个Bit:

void SetBit(char &c,int i,int v)
{
	if(v)
	c |=(1<<i);//设置为1,将1左移到第i位
	else
	c &=~(1<<i);//设置为0,~是取反操作
}

通过与1异或来模拟开关状态:

void FlipBit(char &c,int i)
{
	c^=(1<<i);//^是异或
}

学习到了两个函数:

1.void *memset(void *s, int ch, size_t n):他的作用是将s中当前位置后面的n个字节 (typedef unsigned int size_t )用 ch 替换并返回 s 。位于include<memory>

2.void *memcpy(void *dest, const void *src, size_t n):从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中,位于include<string>

后记:本题还有高斯消元的解法,暂时不会,会了再补













猜你喜欢

转载自blog.csdn.net/qq_33657357/article/details/79604356