非常经典的开开关题目,按一个按钮周围四个方向都会改变,这个题目就是一行一行确定。
大致解题思路:这道题目首先看一眼,我们就可以知道必然与位运算有着密切的关系,因为出现了0和1,这是一个重要的发现,接着我们在仔细分析题意,我们知道如果纯暴力枚举的话,必然是会超时的,那么如何优化呢?因此我们需要从题目中找出非常有用的性质来优化,这是一个大致的思路方向
每一个位置顶多只会操作一次。因为如果操作两次的话,相当于不操作,必然是不满足最优解。
在一套方案中,操作的顺序无关紧要,这一个略加思索便可得知
最重要的性质,如果我们确定了第I行的操作方案的话,那么后面的行数都可以依此递推。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
public class Main {
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static PrintWriter pw = new PrintWriter(System.out);
static int N = 6, n;
static int dx[] = {0, 1, 0, -1, 0};
static int dy[] = {0, 0, -1, 0, 1};
static int g[][] = new int[N][N], backup[][] = new int[N][N];
public static void turn(int x, int y) {
for (int i = 0; i < dx.length; i++) {
int a = x + dx[i];
int b = y + dy[i];
if (a >= 0 && a < 5 && b >= 0 && b < 5) g[a][b] ^= 1;
}
}
public static int dfs() {
int ans = Integer.MAX_VALUE;
for (int k = 0; k < 1 << 5; k++) {
int res = 0;
for (int i = 0; i < 5; i++)
for (int j = 0; j < 5; j++)
backup[i][j] = g[i][j];
for (int i = 0; i < 5; i++)
if (((k >> i) & 1) == 1) {
res++;
turn(0, i);
}
for (int i = 0; i < 4; i++)
for (int j = 0; j < 5; j++)
if (g[i][j] == 0) {
res++;
turn(i + 1, j);
}
boolean success = true;
for (int i = 0; i < 5; i++)
if (g[4][i] == 0) {
success = false;
break;
}
if (success) ans = Math.min(ans, res);
for (int i = 0; i < 5; i++)
for (int j = 0; j < 5; j++)
g[i][j] = backup[i][j];
}
if (ans > 6) return -1;
return ans;
}
public static void main(String[] args) throws IOException {
n = Integer.parseInt(br.readLine());
for (int i = 0; i < n; i++) {
for (int j = 0; j < 5; j++) {
String s = br.readLine();
for (int k = 0; k < 5; k++) {
g[j][k] = s.charAt(k) - '0';
}
}
pw.println(dfs());
br.readLine();
}
pw.flush();
pw.close();
br.close();
}
}