题意:一个n*n的矩阵,每次将左上角元素染色,则所有与它连通(两元素连通的条件是有公共边)的元素都被染为相同颜色,问至少需要多少次操作使得所有元素颜色相同
解法:明显的ida*,估价函数为当前矩阵元素的种类数减一,如果每次向外扩展状态时都暴力枚举会tle,因为很多状态是无用的(即不会使连通块扩大),因此用一个vis数组记录每个元素的状态,1代表已与左上角元素连通(无需扩展),2代表不与左上角连通但与某个连通的元素相邻(待扩展),0代表其它。每层选择颜色时只选择vis值为2的元素中包含的颜色。
通过本题对ida*写法有了一些体会:
1.尽量不扩展使下层h函数不变的无用状态,尽量避免重复状态的枚举(状态少时可以给每个状态一个hash值),这条剪枝很高效
2.发现无用状态后要立即恢复现场,return之前完成回溯,否则可能导致状态混乱。
3.h()估价函数复杂度不应太高,且应尽量准确(减少迭代次数)
import java.util.Arrays; import java.util.Scanner; public class Flood4127 { void flood(int x, int y, int c) { if (x < 1 || y < 1 || x > n || y > n) return; if (vis[x][y] == 1) return; if (map[x][y]!=c) { vis[x][y]=2; return; } vis[x][y] = 1; flood(x - 1, y, c); flood(x + 1, y, c); flood(x, y - 1, c); flood(x, y + 1, c); } boolean color[] = new boolean[10]; int h() { int res = 0; Arrays.fill(color, false); for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) if (vis[i][j]!=1&&!color[map[i][j]]) { res++; color[map[i][j]] = true; } return res; } int lim; boolean flag, de; int dfs(int d) { int h = h(); if (h == 0) { flag = true; return d; } if (d+h>lim) return d + h; int res = 1 << 28; int tvis[][] = new int[10][10]; for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) tvis[i][j] = vis[i][j]; for (int k = 0; k < 6; k++) { int cnt=0; for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) if (vis[i][j] == 2&&map[i][j]==k){ flood(i,j,k); cnt++; } if(cnt==0) continue; int tp = dfs(d + 1); for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) vis[i][j] = tvis[i][j]; if (flag) return tp; res = Math.min(tp, res); } return res; } int map[][] = new int[10][10], n; int vis[][] = new int[10][10]; Scanner scan = new Scanner(System.in); void work() { flag = false; for (int i = 1; i <= n; i++) Arrays.fill(vis[i], 0); flood(1,1,map[1][1]); lim = h(); while (!flag) lim = dfs(0); System.out.println(lim); } void run() { while (true) { n = scan.nextInt(); if (n == 0) break; for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) map[i][j] = scan.nextInt(); work(); } } public static void main(String[] args) { new Flood4127().run(); } }