当前位置: 代码迷 >> 综合 >> POJ 3279 Filptile 题解 状态压缩枚举
  详细解决方案

POJ 3279 Filptile 题解 状态压缩枚举

热度:31   发布时间:2023-12-08 00:15:28.0

题目大意

给定一个M*N的网格,上面有黑白块,然后每次可以反转某个块及其上下左右的块,求整个网格全部变为白色的最少反转次数,如果没有办法反转成功就输出IMPOSSIBLE。

分析

解决这道问题的关键是,如何分析各个块之间的依赖关系,如果直观地来看各个相邻的块似乎是互相依赖的,因为两个相邻格子可以通过反转的方式互相修改对方的颜色,这种两变量互相影响的问题我们可以通过数学里边的主元法来思考问题。

如果两个变量互相依赖我们考虑将其中之一定下来,假设第一行的块被确定下来的话,如果将其变为全白色,则只能通过反转第二行(第一行黑色块的下面那一个块)完成,这样我们确定了第二行需要反转哪些格子,此时如果将第二行的黑色反转为白色的话,只能通过第三行,以此类推,如果当最后一行的反转方案确定以后,此时前m-1行应该是全为白色,如果最后一行也为全白色,那么即可完成,否则失败。

而如何将第一行的状态确定下来呢?可以考虑采用枚举法,共有n个格子,则有2^n种方案,可以通过状态压缩枚举0~2^n-1,然后利用二进制对第一行每个格子赋值,然后再求出整个棋盘的状态,这样最终的算法复杂度为O(2^N * M * N)。

源代码

import java.io.*;
import java.util.*;public class Main {public static final int maxn = 25;public static int maze[][] = new int[maxn][maxn];public static int t_maze[][] = new int[maxn][maxn];public static int temp[][] = new int[maxn][maxn];public static int ans[][] = new int[maxn][maxn];public static final int dx[] = {0, -1, 0, 1, 0};public static final int dy[] = {0, 0, 1, 0, -1};public static boolean succ = false;public static int m, n, ans_cnt, cnt;public static void change(int x, int y) {for(int i = 0; i < 5; i++) {int tx = x + dx[i];int ty = y + dy[i];if(tx >= 0 && tx < m && ty >= 0 && ty < n) {t_maze[tx][ty] = 1 - t_maze[tx][ty];}}}public static boolean check() {for(int j = 0; j < n; j++) {if(t_maze[m - 1][j] == 1) {return false;}}succ = true;return true;}public static void solve() {succ = false;ans_cnt = Integer.MAX_VALUE;for(int i = 0; i < (1 << n); i++) {cnt = 0;for(int j = 0; j < m; j++) {for(int k = 0; k < n; k++) {t_maze[j][k] = maze[j][k];}}int first_state = i;for(int j = 0; j < n; j++) {temp[0][n - j - 1] = first_state & 1;first_state >>= 1;}for(int j = 1; j < m; j++) {for(int k = 0; k < n; k++) {if(temp[j - 1][k] == 1) {change(j - 1, k);}}     for(int k = 0; k < n; k++) {if(t_maze[j - 1][k] == 1) {temp[j][k] = 1;} else {temp[j][k] = 0;}}}for(int j = 0; j < n; j++) {if(temp[m - 1][j] == 1) {change(m - 1, j);}}for(int j = 0; j < m; j++) {for(int k = 0; k < n; k++) {if(temp[j][k] == 1) {cnt++;}}}if(cnt < ans_cnt && check()) {ans_cnt = cnt;for(int j = 0; j < m; j++) {for(int k = 0; k < n; k++) {ans[j][k] = temp[j][k];}}}}if(succ) {for(int i = 0; i < m; i++) {for(int j = 0; j < n - 1; j++) {System.out.print(ans[i][j] + " ");}System.out.println(ans[i][n - 1]);}} else {System.out.println("IMPOSSIBLE");}}public static void main(String[] args) {InputStream inputStream = System.in;OutputStream outputStream = System.out;InputReader in = new InputReader(inputStream);PrintWriter out = new PrintWriter(outputStream);m = in.nextInt();n = in.nextInt();for(int i = 0; i < m; i++) {for(int j = 0; j < n; j++) {maze[i][j] = in.nextInt();}}solve();out.close();}static class InputReader {public BufferedReader reader;public StringTokenizer tokenizer;public InputReader(InputStream stream) {reader = new BufferedReader(new InputStreamReader(stream), 32768);tokenizer = null;}public String next() {while (tokenizer == null || !tokenizer.hasMoreTokens()) {try {tokenizer = new StringTokenizer(reader.readLine());} catch (IOException e) {throw new RuntimeException(e);}}return tokenizer.nextToken();}public int nextInt() {return Integer.parseInt(next());}}
}