POJ - 1222. EXTENDED LIGHTS OUT(熄灯问题)(二进制枚举)
题目链接
题目大意
有一个由按钮组成的矩阵,其中每行有6
个按钮,共5
行。每个按钮的位置上有一盏灯。当按下一个按钮后,该按钮以及周围位置(上边、下边、左边、右边)的灯都会改变一次。即,如果灯原来是点亮的,就会被熄灭;如果灯原来是熄灭的,则会被点亮。在矩阵角上的按钮改变3盏灯的状态;在矩阵边上的按钮改变4盏灯的状态;其他的按钮改变5盏灯的状态。
对矩阵中的每盏灯设置一个初始状态。请你按按钮,直至每一盏等都熄灭。与一盏灯毗邻的多个按钮被按下时,一个操作会抵消另一次操作的结果。
Sample Input
5
行组成,每一行包括6
个数字(0
或1
)。相邻两个数字之间用单个空格隔开。0
表示灯的初始状态是熄灭的,1
表示灯的初始状态是点亮的。
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
5
行组成,每一行包括6
个数字(0
或1
)。相邻两个数字之间用单个空格隔开。其中的1
表示需要把对应的按钮按下,0
则表示不需要按对应的按钮。
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
解析
如果枚举每一个格子的每两个状态,就会有230种状态,会超时。
正确的做法是只需要枚举第一行的状态,然后依次往下面去影响下面的行,只枚举一行的话,只需要26 = 64种状态,可以使用二进制枚举的方式来进行枚举:
- 使用一个字符数组保存每一行的一个状态即可,因为这个数最大不会超过 26,每一个字符保存的是一行的状态,因为可以使用一行
6
位来确定这个字符; - 设定三个函数
getBit
、setBit
、flipBit
分配来获取字符的第i
位、设置字符的第i
位、翻转字符的第i
位。 - 然后按照枚举每一行,然后去影响旁边和下一行的方式,总共枚举
64
种即可,如果枚举到第5
行,这一行的灯都是0
(灭)的话,这个方案就可以,记得使用一个字符数组记录结果,最后打印即可。
import java.io.*;
import java.util.Scanner;
public class Main {
static PrintStream out;
static Scanner in;
static int getBit(char c, int i) {
return (c >> i) & 1;
}
static void setBit(char[] lights, int i, int j, int val) {
lights[i] = (char) (val == 1 ? lights[i] | (1 << j) : lights[i] & (~(1 << j)));
}
static void flipBit(char[] lights, int i, int j) {
lights[i] ^= (1 << j);
}
static void printResult(int t, char[] res) {
out.println("PUZZLE #" + t);
for (int i = 0; i < 5; ++i) {
for (int j = 0; j < 6; ++j) {
out.print(getBit(res[i], j));
if (j < 5)
out.print(" ");
}
out.println();
}
}
static void solve() {
char[] oriLight = new char[5];
char[] lights = new char[5];
char[] res = new char[5];
int T, num;
T = in.nextInt();
for (int t = 1; t <= T; ++t) {
for (int i = 0; i < 5; ++i) {
for (int j = 0; j < 6; ++j) {
num = in.nextInt();
setBit(oriLight, i, j, num);
}
}
for (int n = 0; n < 64; n++) {
int switchs = n;
System.arraycopy(oriLight, 0, lights, 0, oriLight.length);
for (int i = 0; i < 5; ++i) {
res[i] = (char) switchs;
for (int j = 0; j < 6; ++j) {
if (1 == getBit((char) switchs, j)) {
flipBit(lights, i, j);
if (j > 0)
flipBit(lights, i, j - 1);
if (j < 5)
flipBit(lights, i, j + 1);
}
}
if (i < 4)
lights[i + 1] ^= switchs;
switchs = lights[i];
}
if (lights[4] == 0) {
printResult(t, res);
break;
}
}
}
}
public static void main(String[] args) {
in = new Scanner(new BufferedInputStream(System.in));
out = System.out;
solve();
}
}
C++
代码:
#include <iostream>
#include <string.h>
int getBit(char c, int i){ //取c的第i位
return (c>>i) & 1;
}
void setBit(char &c, int i, int val){ //给c的第i位设置成val(0/1)
c = val ? c|(1<<i) : c&(~(1<<i));
}
void flipBit(char &c, int i){ // 将c的第i位翻转
c ^= (1 << i); //不同为1相同为0, 原先是1 ^ 1--> 0, 原先是0^1 --> 1
}
void printResult(int t, const char* res){
std::cout << "PUZZLE #" << t << std::endl;
for(int i = 0; i < 5; i++){
for(int j = 0; j < 6; j++){
std::cout << getBit(res[i], j);
if(j < 5)
std::cout << " ";
}
std::cout << std::endl;
}
}
int main(int argc, char const** argv)
{
std::ios::sync_with_stdio(false);
std::cin.tie(0);
int T, num;
char oriLights[5], lights[5], res[5];
std::cin >> T;
for(int t = 1; t <= T; t++){
for(int i = 0; i < 5; i++){ // 5 row
for(int j = 0; j < 6; j++){ // 6 col
std::cin >> num;
setBit(oriLights[i], j, num);
}
}
// 枚举当前的行,去处理下面的行,二进制枚举总共有2^6 = 64
for(int n = 0; n < 64; n++){
int tmpN = n; // cur row's result
memcpy(lights, oriLights, sizeof(oriLights));
for(int i = 0; i < 5; i++){
res[i] = tmpN; // record the result
for(int j = 0; j < 6; j++){
if(getBit(tmpN, j)){ //judge curPosition is 1, to influence next to self
flipBit(lights[i], j); // self filp
if(j > 0)
flipBit(lights[i],j-1); // left filp
if(j < 5)
flipBit(lights[i],j+1); // right flip
}
}
//curRows influence next row (important) ---> down flip
if(i < 4) // 5 rows in total
lights[i+1] ^= tmpN;
tmpN = lights[i]; // update tmpN
}
if(lights[4] == 0){
printResult(t, res);
break;
}
}
}
return 0;
}