对于每组数据都跑一边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,于是我拿到了运行时间倒数第二qwq
2.有技巧的枚举(参考博客)
看看本题提交记录,居然有6ms通过的!原来是对于每组数据,先枚举第一行反转哪些点,然后固定第一行,通过第二行反转使第一行全亮,同样这样使前4行全亮,再看最后一行是否已经全亮,从而更新答案。时间复杂度约为O(T×31×20),通过枚举我们实现了质的飞跃。
(用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); } }