题意:给出一个\(5×5\)矩形网格图,\(a[i][j]\)表示第i行第j列的数字(只能为0或者1),每次操作可以选择一个位置,对于该个位置以及其上下左右个一个位置上的数字0变成1,1变成0,询问是否能少于6次将所有数字变为1,如果能,请输出最少次数,否则输出“-1”.
分析:思路一:倒序BFS:把每个状态看成一个25位的二进制数,从最终状态也就是全部位都为1开始BFS,把6步之内能走到的状态用map记下,就可以\(O(1)\)回答每个询问了.但是BFS会超时...但还是放一下代码,看看能不能优化.
//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<map>
#include<queue>
using namespace std;
inline int read(){
int x=0,o=1;char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
if(ch=='-')o=-1,ch=getchar();
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*o;
}
map<int,int>Map;
queue<int>q;
inline int get_map(int x,int i){//对于状态x,我按下第i位,会变成什么状态?
x=x^(1<<i);
if((i%5)<4)x=x^(1<<(i+1));
if(i%5)x=x^(1<<(i-1));
if(i>=5)x=x^(1<<(i-5));
if(i<20)x=x^(1<<(i+5));
return x;
}
inline void bfs(){
q.push((1<<25)-1);Map[(1<<25)-1]=0;
while(!q.empty()){
int now=q.front();q.pop();
if(Map[now]==7)return;
for(int i=0;i<25;++i){
int res=get_map(now,i);
if(!Map[res]){
Map[res]=Map[now]+1;
q.push(res);
}
}
}
}
int main(){
bfs();
int T=read();
while(T--){
int cnt=0;
for(int i=0;i<25;++i){
char ch;cin>>ch;
cnt+=((ch-'0')<<i);
}
if(Map[cnt])printf("%d\n",Map[cnt]);
else puts("-1");
}
return 0;
}
思路二:DFS:挖掘题目性质,若我们固定了第一行,则点击方案唯一,因为若第二行某一位为0,只能通过点击第三行的该位来改变,每一行这样递推下去,如果最后一行的状态不全为1,说明本次对第一行的点击方式不合法.综上,所以我们DFS对第一行点击方式的枚举,然后每次check这次点击方式是否合法就行了,如果合法就比较最小步数.
//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
inline int read(){
int x=0,o=1;char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
if(ch=='-')o=-1,ch=getchar();
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*o;
}
int ans,a[10][10],b[10][10];
inline int check(int now){
int sum=now;
for(int i=1;i<=5;i++)
for(int j=1;j<=5;j++)
b[i][j]=a[i][j];
for(int i=1;i<=4;i++)
for(int j=1;j<=5;j++)
if(!b[i][j]){
++sum;
b[i][j]^=1;
b[i+1][j]^=1;
b[i+1][j-1]^=1;
b[i+1][j+1]^=1;
b[i+2][j]^=1;
}
for(int i=1;i<=5;i++)if(!b[5][i])return 1e9;
return sum;
}
inline void dfs(int lie,int now){
if(lie>5){
ans=min(ans,check(now));
return;
}//第一行点击方式枚举完毕
a[1][lie]^=1;a[1][lie-1]^=1;a[1][lie+1]^=1;a[2][lie]^=1;
dfs(lie+1,now+1);//点击第一行第lie列,步数now+1
a[1][lie]^=1;a[1][lie-1]^=1;a[1][lie+1]^=1;a[2][lie]^=1;
dfs(lie+1,now); //回溯,也就是不点击第一行第lie列
return;
}
int main(){
int T=read();
while(T--){
for(int i=1;i<=5;++i)
for(int j=1;j<=5;++j){
char ch;cin>>ch;
a[i][j]=ch-'0';
}
ans=1e9;dfs(1,0);
if(ans<=6)printf("%d\n",ans);
else puts("-1");
}
return 0;
}