費解的開關
這題做法很簡單,首先考慮什麼情況下需要摁開關,因為每次摁開關都會導致十字區域的開關狀態變,因此, 當第i行第j列的開關是關的情況下,才需要摁第i+1行第j列的開關
假如第一行固定的情況下,例如第一行是11011,那麼第二行的第3列就必須摁一下,此時第一行變成了11111。因為第二行中某一個摁了一下,導致第二行發生變化,例如變成01011,那麼第三行就需要摁第1列和第3列的開關,此時第二行也變成了11111,一直下去
上面說的是第一行固定的情況,但是第一行也是可以摁的,並且摁的方法數有$2^5$種,所以需要先對第一行開關摁的情況進行一個列舉,然後再遞推後面所有行的情況
這裡要注意的是,由於最後一行沒有下一行,所以最後一行的狀態是無法改變的,當我們從上往下摁到最後一行的時候,需要判斷最後一行是否是全1的狀態,如果是,就更新摁的次數(每次取最小值)
import java.util.Scanner; public class Main { static int[][] map = new int[5][5]; static int[][] move = {{0, 0}, {0, 1}, {0, -1}, {1, 0}, {-1, 0}}; public static void main(String[] args) { Scanner cin = new Scanner(System.in); int T = cin.nextInt(); while ((T--) != 0) { for (int i = 0; i < 5; i++) { String tmp = cin.next(); for(int j = 0; j < 5; j++) map[i][j] = tmp.charAt(j) - '0'; } System.out.println(dfs()); } } static int dfs() { int ans = Integer.MAX_VALUE >> 1; for (int i = 0; i < (1 << 5); i++) { int sum = 0; int[][] copy = new int[5][5]; for (int row = 0; row < 5; row++) for (int col = 0; col < 5; col++) copy[row][col] = map[row][col]; for (int j = 0; j < 5; j++) if ((i >> j & 1) == 1) { sum++; change(0, j); } for (int k = 0; k < 4; k++) // 最後一行不摁 for (int col = 0; col < 5; col++) if (map[k][col] == 0) { sum++; change(k + 1, col); } boolean flag = true; for (int col = 0; col < 5; col++) if (map[4][col] == 0) { flag = false; break; } if (flag) ans = Math.min(ans, sum); for (int row = 0; row < 5; row++) for (int col = 0; col < 5; col++) map[row][col] = copy[row][col]; } return ans <= 6 ? ans : -1; } static void change(int x, int y) { for (int i = 0; i < 5; i++) { int nx = x + move[i][0]; int ny = y + move[i][1]; if (nx < 5 && nx >= 0 && ny < 5 && ny >= 0) map[nx][ny] ^= 1; } } }