题解 50920 费解的开关
对于每组数据都跑一边bfs可能会TLE
1.逆向思想+bfs预处理(参考博客)
运用逆向思维,我们可以从灯全亮的状态开始bfs走6步,记录下所有能到达的状态所需步数,相当于预处理,对于每组数据直接输出答案即可。时间复杂度约为O(68408+T*25)(bfs入队68408次)
#include<cstdio>
#include<cstring>
#include<queue>
#include<map>
using namespace std;
queue<int> q;
map<int,int> ans; //注意要用map,如果开ans[1<<25]会MLE,毕竟很多状态是没用的
int T;
inline int flip(int x,int i) { //将以i为中心的上下左右反转,注意边界判断
x^=(1<<i);
if (i%5!=4) x^=(1<<(i+1));
if (i%5) x^=(1<<(i-1));
if (i>4) x^=(1<<(i-5));
if (i<20) x^=(1<<(i+5));
return x;
}
int main() {
scanf("%d",&T);
q.push((1<<25)-1),ans[(1<<25)-1]=1; //全亮的状态
while(!q.empty()) {
int x=q.front();
q.pop();
if (ans[x]==7) break; //只走6步
for (int i=0,y; i<25; i++) //枚举反转哪一盏灯
if (ans[y=flip(x,i)]==0)
q.push(y),ans[y]=ans[x]+1;
}
while(T--) {
int y=0;
for (int i=0,x; i<5; i++)
for (int j=0; j<5; j++)
scanf("%1d",&x),y+=x<<(i*5+j); //把矩阵看成二进制数压缩到y里去(当然也可以用数组存储)
printf("%d\n",ans[y]? ans[y]-1:-1);
}
}
754ms,于是我拿到了运行时间倒数第二
2.有技巧的枚举(参考博客)
看看本题提交记录,居然有6ms通过的!看看代码,原来是对于每组数据,先枚举第一行反转哪些点,然后固定第一行,通过第二行反转使第一行全亮,同样这样使前4行全亮,再看最后一行是否已经全亮,从而更新答案。时间复杂度约为O(T3120),通过枚举我们实现了质的飞跃。
(用c++11运行时不知道为什么答案不正确,改为c++14即可)
#include<cstdio>
using namespace std;
int ans,T;
inline int flip(int &x,int i) {
x^=(1<<i);
if (i%5!=4) x^=(1<<(i+1));
if (i%5) x^=(1<<(i-1));
if (i>4) x^=(1<<(i-5));
if (i<20) x^=(1<<(i+5));
}
void check(int x,int p){
int cnt=0; //反转次数
for (int i=0;i<5;i++)
if ((p>>i)&1)
flip(x,i),++cnt; //根据我们的要求反转第一行
for (int i=0;i<4;i++)
for (int j=0;j<5;j++) //第一行固定了,再依次通过下一行反转使前4行全亮
if (!((x>>(i*5+j))&1)){
flip(x,i*5+j+5);
if (++cnt>=ans)
return;
}
if ((x>>20)==31) ans=cnt; //若最后5位全亮则更新答案
}
int main(){
scanf("%d",&T);
while(T--){
int y=0;
ans=7;
for (int i=0,x; i<5; i++)
for (int j=0; j<5; j++)
scanf("%1d",&x),y=(y<<1)+x; //把矩阵看成二进制数压缩到y里去
for (int state=0;state<32;state++) check(y,state); //用二进制数state从00000到11111(31),枚举哪些点反转(位上是1则反转)
ans==7 ? puts("-1"):printf("%d\n",ans);
}
}