UVA1601 The Morning after Halloween

这篇博客介绍了UVA1601题目的解决方案,该题目涉及一种特殊的广度优先搜索(BFS)问题,其中三个小鬼在包含障碍的地图上移动。文章分析了算法优化点,通过预处理地图并建立可达性图来减少非法移动的检查。代码中详细说明了变量含义,并展示了如何实现多鬼魂同时移动的BFS搜索。
摘要由CSDN通过智能技术生成

UVA1601 The Morning after Halloween

题目链接

做这道题的时候看到一个写的很好的代码,在这里保存下来,以便以后学习。

题目分析

这道题和普通的bfs有所不同,解题方法也有些差别,主要是这里有三个移动的“小鬼”,每个小鬼有五种移动状态(上下左右和不动),最主要的是可以看出在地图上大部分的点都是障碍物,所以可以把所有的空格都提出来建立一张图,而不必每次判断五种方法是否合法,以此来优化算法。

代码解析

本题代码中用到的变量较多,为便于理解,先把主要变量的含义解释一下。

x[i] :第i个空格的横坐标
y[i] :第i个空格的纵坐标
id[i][j] :坐标为(i,j)的空格的编号
s[i]:初始小鬼所在空格的编号
t[i]:小鬼所要到达的目标位置的空格编号
deg[i]:在第i个空格所能走的步数
G[i][j]:在第i个空格所能到达的第j个位置的空格编号

注意事项:
main函数中最后两个if语句的作用是当小鬼的数量小于三个时,默认多出的小鬼初始位置即为目标位置,且他们只能原地不动。
ID函数的作用是将三个变量合成一个变量一次性存到队列中。

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;

int w, h, n, s[3], t[3];
char dataset[20][20];
int G[200][5], vis[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;
}
inline bool conflict(int a, int b, int a2, int b2)
{
	return((a2 == b2) || (a == b2 && b == a2));
}
int bfs()
{
	queue<int>q;
	q.push(ID(s[0], s[1], s[2]));
	dist[s[0]][s[1]][s[2]] = 0;
	while (!q.empty())
	{
		int u = q.front();
		q.pop();
		int a = (u >> 16) & 0xff;
		int b = (u >> 8) & 0xff;
		int c = u & 0xff;
		if (a == t[0] && b == t[1] && c == t[2])
			return dist[a][b][c];
		for (int i = 0; i < deg[a]; i++)
		{
			int a2 = G[a][i];
			for (int j = 0; j < deg[b]; j++)
			{
				int b2 = G[b][j];
				if (conflict(a, b, a2, b2))
					continue;
				for (int k = 0; k < deg[c]; k++)
				{
					int c2 = G[c][k];
					if (conflict(a, c, a2, c2) || conflict(b, c, b2, c2))
						continue;
					if (dist[a2][b2][c2] == -1)
					{
						dist[a2][b2][c2] = dist[a][b][c] + 1;
						q.push(ID(a2, b2, c2));
					}
				}
			}
		}
	}
	return -1;
}
int main()
{
	while (~scanf("%d%d%d\n", &w, &h, &n) && n)
	{
		for (int i = 0; i < h; i++)
			fgets(dataset[i], 20, stdin);
		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 (dataset[i][j] != '#')
				{
					x[cnt] = i;//第cnt个空格的横坐标
					y[cnt] = j;//第cnt个空格的纵坐标
					id[i][j] = cnt;//坐标为(i,j)的空格的编号
					if (islower(dataset[i][j]))
						s[dataset[i][j] - 'a'] = cnt;//初始小鬼位置
					else if (isupper(dataset[i][j]))
						t[dataset[i][j] - 'A'] = cnt;//目标小鬼位置
					cnt++;
				}
			}
		}
		for (int i = 0; i < cnt; i++)
		{
			deg[i] = 0;//在第i个空格所能走的步数
			for (int j = 0; j < 5; j++)
			{
				int nx = x[i] + dx[j];
				int ny = y[i] + dy[j];
				if (dataset[nx][ny] != '#')
					G[i][deg[i]++] = id[nx][ny];//第i个空格所能到达的位置编号
			}
		}
		if (n <= 2)
		{
			deg[cnt] = 1;
			G[cnt][0] = cnt;
			s[2] = t[2] = cnt++;
		}
		if (n <= 1)
		{
			deg[cnt] = 1;
			G[cnt][0] = cnt;
			s[1] = t[1] = cnt++;
		}
		memset(dist, -1, sizeof(dist));
		printf("%d\n", bfs());
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值