zoukankan      html  css  js  c++  java
  • 枚举

    题目描述

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

    在上图中,左边矩阵中用X标记的按钮表示被按下,右边的矩阵表示灯状态的改变。对矩阵中的每盏灯设置一个初始状态。请你按按钮,直至每一盏等都熄灭。与一盏灯毗邻的多个按钮被按下时,一个操作会抵消另一次操作的结果。在下图中,第2行第3、5列的按钮都被按下,因此第2行、第4列的灯的状态就不改变。

    请你写一个程序,确定需要按下哪些按钮,恰好使得所有的灯都熄灭。根据上面的规则,我们知道
    1)第2次按下同一个按钮时,将抵消第1次按下时所产生的结果。因此,每个按钮最多只需要按下一次;
    2)各个按钮被按下的顺序对最终的结果没有影响;
    3)对第1行中每盏点亮的灯,按下第2行对应的按钮,就可以熄灭第1行的全部灯。如此重复下去,可以熄灭第1、2、3、4行的全部灯。同样,按下第1、2、3、4、5列的按钮,可以熄灭前5列的灯。

    输入
    5行组成,每一行包括6个数字(0或1)。相邻两个数字之间用单个空格隔开。0表示灯的初始状态是熄灭的,1表示灯的初始状态是点亮的。
    输出
    5行组成,每一行包括6个数字(0或1)。相邻两个数字之间用单个空格隔开。其中的1表示需要把对应的按钮按下,0则表示不需要按对应的按钮。
    样例输入
    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
    样例输出
    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
    来源
    1222

    解题分析

    如果对所有5 * 6个按钮枚举,一共有2^30中策略,枚举空间太大,可以找到枚举空间的一个状态局部,在这道题中第一行就是一个状态局部,对第一行的开关状态进行枚举,一共有64种开关状态,在第一行按下之后,第一行的灯有些被熄灭了,有些还在亮着,对于那些亮着的等,再在第二行的对应列按下,将第一行的所有灯熄灭,然后第二行还有一些灯亮着,再在第三行进行相同操作,直到按下第5行的灯,这个时候,如果第五行的灯全部熄灭,那么所有灯就灭了,我们就找到了一个按开关的策略。
    从这道题可以看出,在枚举空间较大的时候,可以通过找到一个状态局部,对状态局部进行枚举,进而其他状态就确定下来,或者大幅度减少,这样,枚举空间就可以减少很多。
    在这道题的实现过程中,使用char数组分别保存灯的状态,按钮按下策略,然后每个比特表示灯亮或者灭,按钮按下或者不按下。在i行按钮按下后,i+1行的灯的状态可以用按位异或i行的按钮策略直接得到。

    解题代码

    #include <cstdio>
    #include <cstring>
    
    char oriLights[5];
    char lights[5];
    char result[5];
    
    int GetBit(char c, int i){ //返回c的第i个比特
        return (c >> i) & 1;
    }
    
    void SetBit(char & c, int i, int v){ //将c的第i个比特设置为v
        if(v){
            c |= 1 << i;
        }
        else{
            c &= ~(1 << i);
        }
    }
    
    void FlipBit(char & c, int i){//将c的第i个比特翻转
        c ^= 1 << i;
    }
    
    void OutputResult( char result[]){
        for(int i = 0; i < 5; i++){
            for(int j = 0; j < 6; j++){
                printf("%d", GetBit(result[i], j));
                if(j < 5) printf(" ");
            }
            printf("
    ");
        }
    }
    
    int main(){
        for(int i = 0; i < 5; i++){
            for(int j = 0; j < 6; j++){
                int a;
                scanf("%d", &a);
                SetBit(oriLights[i], j, a);
            }
        }
        for(int i = 0; i < 64; i++){ //这是对第一行按下策略的尝试,不同的比特组合对应不同的整数,这个方法叫做状态压缩
            int switchs = i; //swiths 表示当前行的按下状态
            memcpy(lights, oriLights, sizeof(oriLights)); //将输入灯的状态复制到按下过程中灯的状态lights数组中
            for(int i = 0; i < 5; i++){
                result[i] = switchs;
                for(int j = 0; j < 6; j++){ //对第i行按下按钮后灯的状态进行改变
                    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 = lights[i]; //更新i+1行按下策略
            }
            if(lights[4] == 0){
                OutputResult(result);
                break;
            }
        }
        return 0;
        
    }
    

    最后如果不懂,可以参考:https://www.bilibili.com/video/BV1Ht41187bC?t=30&p=5

  • 相关阅读:
    C# Lambda表达式
    .NET轻量级MVC框架:Nancy入门教程(一)——初识Nancy
    SQL中的case when then else end用法
    WPF gif图片不动问题解决
    async(C# 参考)
    File类 ReadAllBytes() ReadAllLines() ReadAllText()
    二维码生成的常用数据格式
    在chrome console加入jquery库
    Reflector反编译WinForm程序重建项目资源和本地资源
    使用Settings.settings存储用户的个性化配置
  • 原文地址:https://www.cnblogs.com/zhangyue123/p/12699579.html
Copyright © 2011-2022 走看看