用离线bfs反向暴力搜索从结果到所有状态的操作最小次数,用二进制来压缩表示状态。
复杂度为 O()的bfs 乘O(1)的查询,计算次数在3e7次左右,可以接受。
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
void TLE() { ios::sync_with_stdio(false), cin.tie(0), cout.tie(0); }
typedef long long ll;
const int N = 5e7 + 10;
const ll mod = 1000000007;
unordered_map<int, int> vis;
int change(int x, int p)
{
x ^= (1 << p);//此处异或1可以将1变成0,将0变成1,也就起到对指定位置改变开关状态的作用
if (p % 5)
x ^= (1 << (p - 1));
if ((p % 5) < 4)
x ^= (1 << (p + 1));
if (p < 20)
x ^= (1 << (p + 5));
if (p > 4)
x ^= (1 << (p - 5));
return x;
}
void bfs()
{
queue<int> q;
q.push((1 << 25) - 1);//最终状态,所有开关都会被打开
vis[(1 << 25) - 1] = 1;
while (q.size())
{
auto t = q.front();
q.pop();
if (vis[t] == 7)
break;
for (int i = 0; i < 25; i++)
{
int now = change(t, i);//转化为下一状态
if (!vis.count(now))
{
q.push(now);
vis[now] = vis[t] + 1;
}
}
}
}
int main()
{
TLE();
bfs();//离线bfs
int T;
cin >> T;
while (T--)
{
int sum = 0;
for (int i = 0; i < 25; i++)//将字符矩阵转化为一维二进制表示
{
char c;
cin >> c;
sum += ((c - '0') << i);
}
cout << vis[sum] - 1 << endl;
}
return 0;
}
y总递推法
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 6;
char g[N][N], bg[N][N];
int dx[5] = {-1, 0, 1, 0, 0}, dy[5] = {0, 1, 0, -1, 0};
void turn(int x, int y) // 按一下第x行第y列的开关
{
for (int i = 0; i < 5; i ++ )
{
int a = x + dx[i], b = y + dy[i];
if (a < 0 || a >= 5 || b < 0 || b >= 5) continue;
g[a][b] ^= 1;
}
}
int main()
{
int T;
scanf("%d", &T);
while (T -- )
{
for (int i = 0; i < 5; i ++ ) scanf("%s", bg[i]);
int res = 10;
for (int op = 0; op < 32; op ++ )
{
int cnt = 0;
memcpy(g, bg, sizeof g);
// 操作第一行的开关
for (int i = 0; i < 5; i ++ )
if (op >> i & 1)
{
turn(0, i);
cnt ++ ;
}
// 递推出第1~4行开关的状态
for (int i = 0; i < 4; i ++ )
for (int j = 0; j < 5; j ++ )
if (g[i][j] == '0')
{
turn(i + 1, j);
cnt ++ ;
}
// 检查最后一行灯是否全亮
bool success = true;
for (int i = 0; i < 5; i ++ )
if (g[4][i] == '0')
success = false;
if (success && res > cnt) res = cnt;
}
if (res > 6) res = -1;
printf("%d\n", res);
}
return 0;
}