题意:一个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();
}
}