原题链接:活动 - AcWing
看了前几篇题解和y总的视频讲解,总有一个奇怪的点搞不明白,为什么说第一行确定了,第二行就确定了,第三行接下来也就确定了。
不做一个假设本蒟蒻怎么会明白(((
假设O是按过的按钮,X是没被按过的按钮。
1、那么对于这些按钮来说,显而易见不管用什么顺序去按,只要按对了就行。
2、对于这幅图来说第一行正确的状态是10010,也就是说这群按钮成功的解的第一行必定按过第一行的那两个按钮。
3、按按钮是十字形状的,也就是说第一行的亮暗同样取决于第二行的按钮。
对于这幅图来说,当我们已经发现了第一行的正确状态之后,正确的按下第一行的那两个按钮,你就会发现第二行的按钮已经被确定了。
因为如果你再去按第二行的按钮,就会导致第一行的亮暗错误。
以此类推后面也都被确定了。
所以现在问题就被简化成,如何去找出第一行正确的状态。
解空间发现每个按钮只能按或者不按,那枚举一下第一行所有情况(包含错误情况+正确情况)就好了(((
AC代码
#include <bits/stdc++.h>
using namespace std;
int n,a[6][6],b[6][6],ans;
bool t[6];
string s[6];
void turnLight(int x,int y){
b[x][y]^=1;//半加运算
if(x-1>=1)b[x-1][y]^=1;
if(x+1<=5)b[x+1][y]^=1;
if(y+1<=5)b[x][y+1]^=1;
if(y-1>=1)b[x][y-1]^=1;
}
void dfs(int y,bool turn){
t[y]=turn;
if(y+1<=5){//画树
dfs(y+1,true);
dfs(y+1,false);
}else{//一条走到底的空间树情况
int step=0;
for(int i=1;i<=5;++i){//毛一个a过来用
for(int j=1;j<=5;++j){
b[i][j]=a[i][j];
}
}
for(int j=1;j<=5;++j){//第一行的情况
if(t[j])turnLight(1,j),step++;
}
for(int i=2;i<=5;++i){//根据第一行按接下来的2345行
for(int j=1;j<=5;++j){
if(b[i-1][j]==0)turnLight(i,j),step++;
}
}
for(int i=1;i<=5;++i){//如果存在一个暗的那就不是这个情况
for(int j=1;j<=5;++j){
if(b[i][j]==0)return;
}
}
ans=min(ans,step);//找出所有情况里面的最小值
}
}
inline void work(){
for(int i=1;i<=5;++i)
cin>>s[i];
for(int i=1;i<=5;++i)
for(int j=0;j<5;++j)
a[i][j+1]=s[i][j]-48;
ans=INT_MAX;
dfs(1,true);
dfs(1,false);
cout<<(ans>6?-1:ans)<<endl;
}
int main(){
cin>>n;
while(n--)work();
return 0;
}
82ms跑过,比y总三年前的172ms略快一点。
此处递归和解空间树相结合,不懂的可以参考