Bootstrap

POJ - 1222. EXTENDED LIGHTS OUT(熄灯问题)(二进制枚举)

POJ - 1222. EXTENDED LIGHTS OUT(熄灯问题)(二进制枚举)


题目链接
题目大意

有一个由按钮组成的矩阵,其中每行有6个按钮,共5行。每个按钮的位置上有一盏灯。当按下一个按钮后,该按钮以及周围位置(上边、下边、左边、右边)的灯都会改变一次。即,如果灯原来是点亮的,就会被熄灭;如果灯原来是熄灭的,则会被点亮。在矩阵角上的按钮改变3盏灯的状态;在矩阵边上的按钮改变4盏灯的状态;其他的按钮改变5盏灯的状态。

对矩阵中的每盏灯设置一个初始状态。请你按按钮,直至每一盏等都熄灭。与一盏灯毗邻的多个按钮被按下时,一个操作会抵消另一次操作的结果。

Sample Input
5行组成,每一行包括6个数字(01)。相邻两个数字之间用单个空格隔开。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个数字(01)。相邻两个数字之间用单个空格隔开。其中的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位来确定这个字符;
  • 设定三个函数getBitsetBitflipBit分配来获取字符的第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;
}

;