AcWing 95. 费解的开关

第一个难点:如何枚举第一行的操作(指数类型的枚举,可以使用第一题的指数型枚举)
这里使用二进制类型枚举
1 1 0 1 0 = 26
反过来任何一个整数0-65536 也可以用二进制表示
image
第一行有5个数,2的5次方等于32

0-31 代表每一种方案;
怎样看第i位是不是1; i>>k & 1
image

第二个难点: 如何写turn(x,y)函数
加上偏移量
image

第三个问题: 时间复杂度

第四个问题:字符1和0互换
image

//  顺序无所谓
//  每个格子最多按一次
// 枚举第一行,第一行固定完,则第二行的操作是固定的
//  每一行开关的操作,完全被前一行的灯的亮灭状态所决定
// 如何枚举第一行的操作;

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=6;     // 5*5=25

char g[N][N];
char backup[N][N];
int dx[5]={-1,0,1,0,0};
int dy[5]={0,1,0,-1,0};
// 改变十字型方格的5个元素值
void turn(int x,int y){
  for(int i=0;i<5;i++){ // 枚举5个位置
    int a=x+dx[i];
    int b=y+dy[i];
    if(a<0 || a>=5 || b<0 || b>=5 ){  // 判断边界
      continue ; // 出界,不用管
    }
    g[a][b]^=1;  // 把字符的0变为1,把字符的1变为0; 
	//可以使用if或者使用位运算; 0的ascall码是48=110000,1的是49=110001)
  }
}


int main(){
  int T;
  cin>>T;
  while(T--){
    for(int i=0;i<5;i++){
      cin>>g[i];    // 读入棋盘
    }
    int res=10;   //最多走10步,超过6步就无解
    for(int op=0;op<32;op++){  // 枚举所有的方案
      memcpy(backup,g,sizeof(g));  // 备份,在备份的棋盘上面做操作
      int step=0;  // 走的最小步数
	  //第0行的操作
      for(int i=0;i<5;i++){  // 最大不超过5步
        if(op>>i&1){ // 判断当前位置要不要操作
          step++;
          turn(0,i); // 第0行的第i列元素
        }
      }
      for(int i=0;i<4; i++){  //第1行决定第0行, 一直执行到倒数第一行
        for(int j=0;j<5;j++){ // 每一列
          if(g[i][j]=='0'){  // 当前是灭的
            step++;  // 步数加1
            turn(i+1,j);  // 下一行的对应的方格按一下 
          }
        }
      }
      // 判断最后一行是不是全亮
      bool dark =false;  
      for(int i=0;i<5;i++){
        if(g[4][i]=='0'){
          dark=true;
          break;
        }
      }
	  // 如果最后一行全亮
      if(!dark){ 
        res=min(res,step); // 取最小的步数
      }
      memcpy(g,backup,sizeof(g));
    }
	// 无解返回-1
    if(res>6){ 
      res=-1;
    }
    cout<<res<<endl;
  }
  
  
  return 0;
}

posted @ 2022-11-06 18:05  努力、奋斗啊  阅读(41)  评论(0编辑  收藏  举报