题目链接

在cnblogs查看

对于每组数据都跑一边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);
    }
}