# 费解的开关(二进制+递推+思维)

费解的开关(二进制+递推+思维)


  • 题意:5*5的灯阵,每次按一盏灯的开关,并且这盏灯的上下左右也受到相同的影响(0->1,1->0),求使给定灯阵全1的最少步数。

  • 题解:

    • 每盏灯最多点击一次,点击两次相当于没有点击。
    • 最重要的性质:如果我们确定了第1行的灯的情况的话,那么后面的行数都可以依此递推,当前行灭的灯只能由下一行同一列的灯使之点亮。
  • 附上一个写的较好的题解

举个例子
11011
10110
01111
11111
第一行中第三盏灯为0,那么必须通过第二行的第3张灯将其点亮,当前行的灯只能由下一行的灯点亮,这样才不会影响当前行其他灯的情况。所以只需要确定第一行的灯的情况即可,第一行的灯控制第二行灯的点击情况,通过第二行灯将第一行灭的灯点亮后, 第二行灯处于灭状态的灯由第三行点亮,依次类推
  • Code:
//模块化编程思想

#include <bits/stdc++.h>
using namespace std;

#define mem(a) memset((a),0,sizeof(a))
#define fo(i,a,b) for(int (i)=(a);(i)<(b);(i)++)
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)//宏定义,编译时展开,占用编译时间
#define sf(x) scanf("%d",&(x))
const int inf=(0x7f7f7f7f);
const int maxn=3000;

int n;
int mp[5][5];
int dx[]={0,-1,0,1,0};
int dy[]={0,0,1,0,-1};

void turn(int x,int y){
    fo(i,0,5){
        int xx=x+dx[i];
        int yy=y+dy[i];
        if(xx>=0&&xx<5&&yy>=0&&yy<5){
            mp[xx][yy]^=1;
        }
    }
}

char mmp[5][5];
int work(){
    int ans=(1<<30);
    int cnt=0;


	//fo(i)循环枚举第一行灯的所有可能的点击情况,10010(表示点击第一盏灯和第四盏灯) 
    fo(i,0,31){
    	//初始化, 
        fo(x,0,5)fo(y,0,5)mp[x][y]=mmp[x][y]-'0';
        cnt=0;
		
		//通过二进制数解码出灯的点击情况 
        fo(j,0,5)
            if((i>>j)&1){
                cnt++;
                turn(0,j);
            }

		//依次处理前4行灯 
        fo(j,0,4){
            fo(k,0,5){
                if(mp[j][k]==0){
                    turn(j+1,k);//通过下一行的灯使其点亮 
                    cnt++;
                }
            }
        }

		//检查第五行灯是否是全亮的情况,如果不是由于前4行是全1的情况
		//第五行灭的灯无法点亮,故当前方案不合理 
        bool is_ok=1;
        fo(j,0,5)if(mp[4][j]==0){
            is_ok=0;
            break;
        }
        
        if(is_ok)
        ans=min(ans,cnt);

    }

    if(ans>6)return -1;
    return ans;
}

int main(){
    sf(n);
    while(n--){
        fo(i,0,5)scanf("%s",mmp[i]);

        //fo(i,0,5)fo(j,0,5)cout<<mmp[i][j];


        cout<<work()<<endl;
    }

    return 0;
}
posted @ 2019-07-02 22:54  yhsmer  阅读(536)  评论(0编辑  收藏  举报