知识点:递归,递推
这个题有点难度,首先我们来分析题目,每个开关最多只用摁一下,因为摁两次相当于没有摁,以此类推,然后就是假如我们能够将它还原,那么我们按了一些开关,这些摁的的开关的先后次序是不影响的,影响的只有我们摁什么开关能让所有的灯都开开,而不是按这些开关的次序,最后就是,假如我们枚举第一行,后面的行为了能让所有的开关都开开,那么这时的方案是唯一的,这里就是递推,通过第一行的状态我们可以知道后面的行需要怎么按,然后在最后再判断一下最后一行,这样这个题的思路就出来了,
我们枚举第一行的按开关的情况,这个就是枚举第一行的列坐标,是指数型枚举,然后等我们到了递归的边界的时候,开始统计当前的按开关的方案,先按一下第一行,然后按后面的4行,每一行每个位置都是为了让前面的开关是不是开的而决定按不按,最后我们看一下最后一行的开关是不是都开了就行了,这个时间复杂度算一下就可以知道是够的
然后我30分钟看了答案才过的,是因为我递归的边界对所有开关的处理都是在输入数组里面处理的,这样当然是错的,,要用复制过来的数据,,,
#include <bits/stdc++.h>
using namespace std;
int ans;
int dx[4] = {-1, 0, 1, 0};
int dy[4] = {0, 1, 0, -1};
vector<int> v;
string t[5];
void solve(int x) {
if (x == 5) {
int temp = 0;
string s[5];
for (int i = 0; i < 5; i++) s[i] = t[i];
for (int i = 0; i < v.size(); i++) {
s[0][v[i]] = (s[0][v[i]] == '0' ? '1' : '0');
for (int j = 0; j < 4; j++) {
int x1 = 0 + dx[j];
int y1 = v[i] + dy[j];
if (x1 < 0 || x1 >= 5 || y1 < 0 || y1 >= 5) continue;
s[x1][y1] = (s[x1][y1] == '0' ? '1' : '0');
}
}
temp += v.size();
for (int i = 1; i < 5; i++) {
for (int j = 0; j < 5; j++) {
if (s[i - 1][j] == '0') {
s[i][j] = (s[i][j] == '0' ? '1' : '0');
temp++;
for (int k = 0; k < 4; k++) {
int x1 = i + dx[k];
int y1 = j + dy[k];
if (x1 < 0 || x1 >= 5 || y1 < 0 || y1 >= 5) continue;
s[x1][y1] = (s[x1][y1] == '0' ? '1' : '0');
}
}
}
}
int ok = 1;
for (int i = 0; i < 5; i++) {
if (s[4][i] == '0') { ok = 0; break; }
}
if (ok) ans = min(ans, temp);
return;
}
solve(x + 1);
v.push_back(x);
solve(x + 1);
v.pop_back();
}
int main() {
int T;
cin >> T;
while (T--) {
ans = 100000000;
for (int i = 0; i < 5; i++) cin >> t[i];
solve(0);
cout << (ans <= 6 ? ans : -1) << endl;
}
return 0;
}