#pragma GCC optimize(2) // 累了就吸点氧吧~ #include using namespace std; // 5858 ms // 当前状态,当前到最终状态所需步数 unordered_map step; // 改变这个灯及其上下左右相邻的灯的状态 int turn(int t, int idx) { // idx从0开始 t ^= (1 << idx); // 改变第idx个灯,0->1,1->0,互换状态,其它灯不动 if (idx % 5) t ^= (1 << idx - 1); // 不为最左一列,将左侧灯改变 if (idx >= 5) t ^= (1 << idx - 5); // 不为最上一排,将上侧灯改变 if (idx < 20) t ^= (1 << idx + 5); // 不为最后一排,将下面灯改变 if (idx % 5 < 4) t ^= (1 << idx + 1); // 不为最后一列,将右面灯改变 return t; } /* 我们需要知道某个局面是不是出现过,也就是需要把状态记录下来。 而一个二维的数组状态不好记录,我们喜欢记录一维的。 每个位置不是0,就是1,最多25个格子,联想到状态压缩,用二进制来表示状态。 从输入的状态开始,走6步,可以覆盖到哪些数组状态呢?我们可以用状态压缩+Hash表记录下来,最终判断一下 int x = (1 << 25) - 1;这个状态是不是在Hash表中就行了。 也可以使用逆向思维:从最终状态逆序遍历,找出可以在6步之内到达最终状态的所有可行状态,应该是一样的 */ void bfs() { // 0-2^25-1(25个1),共2^25种状态 // 最终的状态 全都是 `11111` int x = (1 << 25) - 1; // 左移 右移的优先级是最低的,比加减还要低。所以这里的括号是必需的 queue q; q.push(x); step[x] = 0; // 记录这个状态是否出现过,如果出现过,是走几步出现的 while (q.size()) { auto t = q.front(); q.pop(); if (step[t] == 6) break; // 反向最多扩展6步 for (int i = 0; i < 25; i++) { x = turn(t, i); // u:当前状态,i:改变i这一位置,x:将此状态改变后的新状态 if (!step.count(x)) { // 过滤过出现过的状态,只要没有出现过的状态 step[x] = step[t] + 1; q.push(x); } } } } int main() { // 预处理 bfs(); int T; scanf("%d", &T); while (T--) { int g = 0; // g是地图 for (int i = 0; i < 25; i++) { char c; // 注意在scanf读取完一个整数,再次读取char时,要清空缓冲区,用" %c"解决 scanf(" %c", &c); g += ((c - '0') << i); // 25个字符二进制压缩成数字 } if (step.count(g) == 0) puts("-1"); else cout << step[g] << endl; } return 0; }