• [openjudge 1813] 熄灯问题 2017-05-10 19:27 89人阅读 评论(1) 收藏


    openjudge 1813 熄灯问题

    描述
    有一个由按钮组成的矩阵,其中每行有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

    题解

    枚举第一行熄灯的情况

    如果上一行是1,则下一行是1

    例:100101 下一行也是 100101 就可以关上一行的灯
    这样推出后面几行

    最后对最后一行进行判断

    如果最后一行都是0就找到了答案 下面是代码

    使用dfs搜索第一行的情况

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    bool a[10][10];//a记录开关灯情况
    bool k[10][10];//原有的开关灯情况
    bool c[10][10];//答案数组
    void turn(int x,int y){//开关灯的函数
        a[x][y]=!a[x][y];
        if(x>=2) a[x-1][y]=!a[x-1][y];
        if(x<=4) a[x+1][y]=!a[x+1][y];
        if(y>=2) a[x][y-1]=!a[x][y-1];
        if(y<=5) a[x][y+1]=!a[x][y+1];
    }
    bool pd(){//判断是否成功关完
        for(int i=1;i<=6;i++)
            if(a[5][i]==1)
            return false;
        return true;
    }
    void deal(){//开关灯
        for(int i=1;i<=6;i++){
            if(c[1][i]==1)
            turn(1,i);
        }
        for(int i=2;i<=5;i++)
        {
            for(int j=1;j<=6;j++)
            if(a[i-1][j]==1)
            {
                c[i][j]=1;
                turn(i,j);
            }
        }
    }
    void makea(){//用a记下k
        for(int i=1;i<=5;i++)
            for(int j=1;j<=6;j++)
                a[i][j]=k[i][j];
    }
    void clean(){//如果失败 将c清零
        for(int i=2;i<=5;i++)
        for(int j=1;j<=6;j++){
            c[i][j]=0;
        }
    }
    bool flag=0;//判断是否成功
    void dfs(int step){//搜索出c[1]的情况
        if(!flag){//成功就退出
            if(step==7){//找出一种c[1]就进行判断
                clean();//清空c
                makea();//用a记下k
                deal();//开关灯
                if(pd()){
                    flag=1;
                }
                return;
            }
            for(int i=0;i<=1;i++)
            {
                c[1][step]=i;
                dfs(step+1);
                if(flag==1) return ;//成功就退出
            }
        }
    }
    int main()
    {
        for(int i=1;i<=5;i++)
            for(int j=1;j<=6;j++)
                scanf("%d",&k[i][j]);
        dfs(1);//从1开始
        for(int i=1;i<=5;i++){//输出
            for(int j=1;j<=6;j++)
                cout<<c[i][j]<<" ";
            cout<<endl;
        }
        return 0;
    }
    

    update 2017 .6 .29

    回档第一篇博客 又是一种心情

    觉得那个时候好辣鸡的啊
    连个dfs都想半天
    学了高斯消元以后 重新来做此题 又花了好长时间(我是制杖吗?。。)
    嗯 高斯消元的做法
    首先:
    1.这个点按奇数次和按一次是一样的 按偶数次和不按是一样的 因为按两次就又回去了
    2.按的顺序和答案没有关系
    列方程:
    一个点最多被五个点影响(上下左右+自己) 因为每个点的次数只是0或者1 所以首先得到方程
    像这个样子x1+x2+x3+x4+x5(mp[i][j]指i,j是否要改变)
    但是mod2不是很好处理了
    又想到 1和 1 变0 0和1 变1 那么这个时候用异或运算就好

    x1^x2^x3^x4^x5=mp[i][j]
    那么 此题是5*6的格子 需要30个未知数 列30个方程 解一下就行

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    int n=5,m=6;int cnt=30;
    int mp[50][50];
    int a[50][50];
    int dx[10]={0,1,-1,0,0};
    int dy[10]={0,0,0,1,-1};
    void gauss(){
        for(int i=1;i<=30;i++){
            int k=i;
            for(;k<=30;k++)
                if(a[k][i]!=0) break;
            swap(a[i],a[k]);
            for(int p=1;p<=30;p++){
                if(p!=i&&a[p][i])
                {
                    for(int j=i;j<=31;j++)
                        a[p][j]=a[p][j]^a[i][j];//异或方程 
                }
            }
        }
    }
    int mk(int x,int y){
        return (x-1)*6+y;
    }
    bool in_Map(int x,int y){
        if(x<1||x>n||y<1||y>m) return false;
        return true;
    }
    void build(){
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
            {
                int plc=mk(i,j);
                a[plc][31]=mp[i][j];//要得到的答案即要改变此点的次数 
                a[plc][plc]=1;
                for(int k=1;k<=4;k++){
                    int tx=i+dx[k];
                    int ty=j+dy[k];
                    if(!in_Map(tx,ty)) continue;
                    a[plc][mk(tx,ty)]=1;
                }
            }
    }
    int main(){
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                scanf("%d",&mp[i][j]);
        build();
        gauss();
        int cnt=0;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++)
                printf("%d ",a[++cnt][31]);
            printf("
    ");
        }
        return 0;
    }
    

    附:poj1222=此题
    代码如下咯:

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    int n=5,m=6;int cnt=30;
    int mp[50][50];
    int a[50][50];
    int dx[10]={0,1,-1,0,0};
    int dy[10]={0,0,0,1,-1};
    void gauss(){
        for(int i=1;i<=30;i++){
            int k=i;
            for(;k<=30;k++)
                if(a[k][i]!=0) break;
            swap(a[i],a[k]);
            for(int p=1;p<=30;p++){
                if(p!=i&&a[p][i])
                {
                    for(int j=i;j<=31;j++)
                        a[p][j]=a[p][j]^a[i][j];//异或方程 
                }
            }
        }
    }
    int mk(int x,int y){
        return (x-1)*6+y;
    }
    bool in_Map(int x,int y){
        if(x<1||x>n||y<1||y>m) return false;
        return true;
    }
    void build(){
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
            {
                int plc=mk(i,j);
                a[plc][31]=mp[i][j];//要得到的答案即要改变此点的次数 
                a[plc][plc]=1;
                for(int k=1;k<=4;k++){
                    int tx=i+dx[k];
                    int ty=j+dy[k];
                    if(!in_Map(tx,ty)) continue;
                    a[plc][mk(tx,ty)]=1;
                }
            }
    }
    int main(){
        int T;
        scanf("%d",&T);
        int nt=0;
        while(T--){
            printf("PUZZLE #%d
    ",++nt);  
            memset(a,0,sizeof(a));
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                scanf("%d",&mp[i][j]);
        build();
        gauss();
        int cnt=0;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++)
                printf("%d ",a[++cnt][31]);
            printf("
    ");
        }
    }
        return 0;
    }
    
  • 相关阅读:
    无法重用Linq2Entity Query
    The Joel Test
    MSBuilder directly instead of default VSComplie with keyborad shotcut 原创
    客户端缓存(Client Cache)
    关于代码重构和UT的一些想法,求砖头
    ExtJS2.0实用简明教程 应用ExtJS
    Perl information,doc,module document and FAQ.
    使用 ConTest 进行多线程单元测试 为什么并行测试很困难以及如何使用 ConTest 辅助测试
    史上最简单的Hibernate入门简介
    汽车常识全面介绍 传动系统
  • 原文地址:https://www.cnblogs.com/xljxlj/p/7183674.html
Copyright © 2020-2023  润新知