uva1601the morning after hallowee(双向BFS经典题)

关于bfs代码里面已经详细注释了,对于这个题,个人理解是除了双向bfs加深了理解以外,更需要注意的是隐式图的建立和一种直觉吧。怎样能看出隐式图,和如果建立,还需要多磨砺。

#include<iostream>
#include<queue >
#include<algorithm>
#include<cstring>
using namespace std;
int w, h, n;
int s[3], t[3]; //表示初态和末态的状态。
char data1[20][20];
int g[200][5];//每个点有5种移动的方式。
int color[200][200][200], dist[200][200][200];  //后面解释
int deg[200];//记录每个格子有多少种可以走的方式。
int dx[] = { 0,-1,1,0,0 };
int dy[] = { 0,0,0,1,-1 };

inline int ID(int a, int b, int c) {
	return (a << 16) | (b << 8) | c;   //a,b,c分别是其各自所在空格的编号,编号要注意方法,比如。12,7,5,可以变成一个整数1275,但是如何把1275变回来 就不好确定了。
}                                       //   所以要掌握好这种二进制压缩的方法
inline bool conflict(int a, int b, int a1, int b1) {
	return ((a1 == b1) || (a == b1) && (b == a1));     //第一个表示不能重叠位置,第二个表示不能交换位置。
}
int bfs() {
	queue<int> qf;
	queue<int> qb;

	dist[s[0]][s[1]][s[2]] = 0;     //标记走到该状态需要多少步
	dist[t[0]][t[1]][t[2]] = 0;

	qf.push(ID(s[0], s[1], s[2]));
	qb.push(ID(t[0], t[1], t[2]));

	color[s[0]][s[1]][s[2]] = 1;     //0表示 没有走过,所以用1.2标记
	color[t[0]][t[1]][t[2]] = 2;
	bool is;
	while (!qf.empty() && !qb.empty()) {
		int num, u;
		if (qf.size() < qb.size() || qb.empty()) {     //找结点少的开始bfs。
			num = qf.size();
			is = true;
		}
		else {
			num = qb.size();
			is = false;
		}
		while (num--) {
			if (is) {
				u = qf.front();
				qf.pop();
			}
			else {
				u = qb.front();
				qb.pop();
			}
			int a = (u >> 16) & 0xff, b = (u >> 8) & 0xff, c = u & 0xff;
			for (int i = 0; i < deg[a]; ++i) {
				int a1 = g[a][i];
				for (int j = 0; j < deg[b]; ++j) {
					int b1 = g[b][j];
					if (conflict(a, b, a1, b1)) continue;
					for (int k = 0; k < deg[c]; ++k) {
						int c1 = g[c][k];
						if (conflict(a, c, a1, c1) || conflict(c, b, c1, b1)) continue;
						if (color[a1][b1][c1] == 0) {
							dist[a1][b1][c1] = dist[a][b][c] + 1;
							if (is) {
								color[a1][b1][c1] = 1;
								qf.push(ID(a1, b1, c1));
							}
							else {
								color[a1][b1][c1] = 2;
								qb.push(ID(a1, b1, c1));
							}

						}
						else if (color[a1][b1][c1] + color[a][b][c] == 3) {
							return dist[a][b][c] + dist[a1][b1][c1] + 1;
						}
						else {
							continue;               //如果有重复的状态则跳过,不用push.
						}
					}
				}
			}
		}

	}
	return -1;
}
int main() {
	while (~scanf("%d%d%d\n", &w, &h, &n) && n) {
		freopen("input.txt", "r", stdin);
		for (int i = 0; i < h; i++)  fgets(data1[i], 20, stdin);   //此处不能用scanf来处理输入,会TLE

		int cnt = 0, x[200], y[200], id[20][20];    //从图中抽取出空间并求出初始状态和目标状态
		for (int i = 0; i < h; i++)
			for (int j = 0; j < w; j++) {
				if (data1[i][j] != '#') {
					x[cnt] = i; y[cnt] = j; id[i][j] = cnt;
					if (islower(data1[i][j]))  s[data1[i][j] - 'a'] = cnt;       //初始状态
					else if (isupper(data1[i][j])) t[data1[i][j] - 'A'] = cnt;   //目标状态
					cnt++;  //注意这里的cnt++不能偷懒在上面一行末尾,因为这样有时候cnt++会没有执行
				}
			}


		for (int i = 0; i < cnt; i++) {  //利用空格建立图
			deg[i] = 0;
			for (int j = 0; j < 5; j++) {
				int nx = x[i] + dx[j];  int ny = y[i] + dy[j];
				if (data1[nx][ny] != '#')  g[i][deg[i]++] = id[nx][ny];
			}
		}

		if (n <= 2) { deg[cnt] = 1; g[cnt][0] = cnt; s[2] = t[2] = cnt++; }     //如果少于3个小鬼,就当作少的那些小鬼一开始就重合了,就全部当作一共3个小鬼算
		if (n <= 1) { deg[cnt] = 1; g[cnt][0] = cnt; s[1] = t[1] = cnt++; }      //不然的话就会有s[2],s[1],这种状态,要统一起来,就只有s[3]这三种。

		memset(dist, 0, sizeof(dist));
		memset(color, 0, sizeof(color));

		if (s[0] == t[0] && s[1] == t[1] && s[2] == t[2])    printf("0\n");
		else    printf("%d\n", bfs());
	}
	return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值