poj 4007 \ hdu 4127 Flood-it!

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


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值