费解的开关
分析:
首先我们得明白如下两点:
1)正解按开关的顺序是可以打乱的,只要这些开关都按了即可;
2)每一个开关最多只按一次,因为按两次的话相当于没按,由于找最小所以按同一个开关多次没有意义。
假如运用暴力法,即每个位置上都按一次不按一次的枚举一遍的话,其时间复杂度为:
(2^25-1)×5×500
(每个开关按下之后会改变5个位置的开关,题目中最多可以测量500组数据)
因此时间大约是83,886,077,500
这个复杂度过于庞大了。
因此再仔细的观察的话,我们发现如下的规则:
只要第一行的开关方案数确定了就可以确定后面的方案数。
因此此时我们只需要枚举第一行的方案数,之后下面的每一行根据上面一行的开关来进行确定开还是关即可;
需要注意的是这样操作一番之后,最后一行假如还是有开关是关着的话就说明此种方案失败了。
此种方法的时间大约为,(2^5-1)×25×5×500
(总共有2^5-1总方案,有25个灯泡,每个灯泡的开和关都会有影响到相邻的4个因此要乘5,有500组数据)
因此时间大约是1,937,500
,相较之下这个真的是小的多了~~
代码及注释
#include<iostream>
#include<cstring>
using namespace std;
const int N=5;
char g[N][N+1],backup[N][N+1];//之所以列多开一列,是为了存储字符串的'\0'
int dx[]={1,0,-1,0,0};
int dy[]={0,1,0,-1,0};
void turn(int x,int y){
for(int i=0;i<5;i++){
int a=x+dx[i],b=y+dy[i];
if(a>=0&&a<5&&b>=0&&b<5) g[a][b]=g[a][b]^1;//和'0''1'的二进制的特点有关
}
}
int main(){
int n;
scanf("%d",&n);
while(n--){
int res=10;//用于确定最终所用的步数
for(int i=0;i<5;i++) cin>>g[i];//读取一行字符串
for(int i=0;i<32;i++){//枚举第一行每个开关的选择的情况,公有2^5-1总情况
int step=0;//用于记录当前已经按了多少个开关
memcpy(backup,g,sizeof g);//把g备份一下
for(int j=0;j<5;j++){
if(i>>j&1){
step++;
turn(0,j);//把第0行j列的等打开
}
}
for(int j=0;j<4;j++){
for(int k=0;k<5;k++){
if(g[j][k]=='0'){
step++;
turn(j+1,k);
}
}
}
bool ispass=true;
for(int j=0;j<5;j++) if(g[4][j]=='0') ispass=false;//如果最后一行还有开关是亮着的那么该方案就是不可取的
if(ispass) res=min(res,step);
memcpy(g,backup,sizeof backup);
}
if(res>6) cout<<-1<<endl;//大于6步的话方案无解
else cout<<res<<endl;
}
return 0;
}
不是应该是2^5吗,视频中之所以是2^5 - 1,是因为下标0开始,0~31,所以是32种第一行的按法,不用-1
清楚了,谢谢大佬