费解的开关(递推+位运算)

    xiaoxiao2022-07-13  154

    你玩过“拉灯”游戏吗?25盏灯排成一个5x5的方形。每一个灯都有一个开关,游戏者可以改变它的状态。每一步,游戏者可以改变某一个灯的状态。游戏者改变一个灯的状态会产生连锁反应:和这个灯上下左右相邻的灯也要相应地改变其状态。

    我们用数字“1”表示一盏开着的灯,用数字“0”表示关着的灯。下面这种状态

    10111 01101 10111 10000 11011 在改变了最左上角的灯的状态后将变成:

    01111 11101 10111 10000 11011 再改变它正中间的灯后状态将变成:

    01111 11001 11001 10100 11011 给定一些游戏的初始状态,编写程序判断游戏者是否可能在6步以内使所有的灯都变亮。

    输入格式 第一行输入正整数nn,代表数据中共有nn个待解决的游戏初始状态。

    以下若干行数据分为nn组,每组数据有5行,每行5个字符。每组数据描述了一个游戏的初始状态。各组数据间用一个空行分隔。

    输出格式 一共输出nn行数据,每行有一个小于等于6的整数,它表示对于输入数据中对应的游戏状态最少需要几步才能使所有灯变亮。

    对于某一个游戏初始状态,若6步以内无法使所有灯变亮,则输出“-1”。

    数据范围 0<n≤5000<n≤500 输入样例: 3 00111 01011 10001 11010 11100

    11101 11101 11110 11111 11111

    01111 11111 11111 11111 11111 输出样例:

    3 2 -1

    思路:枚举第一排按开关的所有状态(利用位运算枚举),然后从第一至第四排所有灯的状态,如果关着,就按一下它下面的灯的开关(最后一排不需要枚举,因为它下面没有灯)。遍历完以后看看最后一排的灯是否都开着,如果是的话,说明操作合法,维护一下最小值。

    #include<iostream> #include<cstring> using namespace std; #define inf 10000000 char a[10][10]; int nex[5][2]={{0,0},{0,1},{0,-1},{1,0},{-1,0}}; void turn(int x,int y) //改变周围五个位置的状态 { for(int i=0;i<5;i++) { int xx=x+nex[i][0]; int yy=y+nex[i][1]; if(xx>=0 && xx<5 && yy>=0 && yy<5) { a[xx][yy]^=1; } } } int work() { int ans=inf; for(int k=0;k< 1<<5;k++) //按位枚举第一排按灯的所有可能性 { char tem[10][10]; memcpy(tem,a,sizeof tem); //保留状态,枚举完以后会用到 int res=0; for(int i=0;i<5;i++) if(k>>i & 1) //如果k的第i位是1,第一排的第i位需要被按一下 { turn(0,i); res++; } for(int i=0;i<4;i++) //枚举1-4for(int j=0;j<5;j++) { if(a[i][j]=='0') { turn(i+1,j); res++; } } int flag=0; for(int i=0;i<5;i++) //判断操作是否合法 if(a[4][i]=='0') { flag=1;break; } memcpy(a,tem,sizeof a); //恢复状态 if(flag) continue; ans=min(ans,res); } if(ans>6) return -1; return ans; } int main() { int n; cin>>n; while(n--) { for(int i=0;i<5;i++) cin>>a[i]; cout<<work()<<endl; } return 0; }
    最新回复(0)