隐式图--UVA - 1601 The Morning after Halloween

本文介绍了一种解决迷宫中特定字符从起点到终点最短路径问题的算法。通过构建空白格子图并利用BFS搜索技术实现路径查找。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

分析:以当前3个小写字母的位置为状态,则问题转化为图的最短路问题。状态总数为256^3,每次转移需要5^3枚举每个小写字母下一步的走法,但是容易超时。由条件“任何一个2×2子网格中至少有一个障碍格”暗示着很多格子都是障碍,并且大部分空地都和障碍相邻,因此不是所有4个方向都能移动,因此可以把所有空格提出来建一个图。这里将原图中的空白点映射为编号,再将三个点的编号合一。预处理可以把每个空白格子的5个方向的下个格子的编号算出来。


#include <cstdio>
#include <cctype>
#include <cstring>
#include <queue>

using namespace std;
const int maxs = 20;
const int maxn = 150;
const int dr[] = { -1, 0, 1, 0, 0 };
const int dc[] = { 0, 1, 0, -1, 0 };
int deg[maxn], G[maxn][5]; //deg[i]表示第i个空白格子的方向个数  G[i][j]表示第i个空白格子的第j个方向的格子;
int vis[maxn][maxn][maxn];
int n, w, h, s[3], t[3];

//a,b,c是空白格子编号,小于150,在一个字节内
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 && a2==b);
}

int bfs()
{
	queue<int> q;
	memset(vis, -1, sizeof(vis));
	q.push(ID(s[0], s[1], s[2]));
	vis[s[0]][s[1]][s[2]] = 0;
	while(!q.empty()) {
		int u = q.front(); q.pop();
		int a = (u>>16)&0xff, b = (u>>8)&0xff, c = u&0xff;
		if(a==t[0] && b==t[1] && c==t[2]) return vis[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)) continue;
					if(conflict(b, c, b2, c2)) continue;
					if(vis[a2][b2][c2] != -1) continue;
					vis[a2][b2][c2] = vis[a][b][c] + 1;
					q.push(ID(a2, b2, c2));
				}
            }
		}
	}
	return -1;
}

int main()
{
	while(scanf("%d%d%d", &w, &h, &n)&&w) {
		char maze[20][20];
		//cnt空白格子个数  id空白格子到编号的映射
		int cnt = 0, x[maxn], y[maxn], id[maxs][maxs];
		for(int i=0; i<h; i++) {
			getchar();
			for(int j=0; j<w; j++) {
				maze[i][j] = getchar();
				if(maze[i][j] != '#') {
					x[cnt] = i; y[cnt] = j; id[i][j] = cnt;
					if(islower(maze[i][j])) s[maze[i][j]-'a'] = cnt;
					else if(isupper(maze[i][j])) t[maze[i][j]-'A'] = cnt;
					cnt++;
				}
			}
		}

		//给空白格子的每个方向做映射
		for(int i=0; i<cnt; i++) {
			deg[i] = 0;
			for(int dir=0; dir<5; dir++) {
				int nx = x[i] + dr[dir], ny = y[i] + dc[dir];
				if(maze[nx][ny] != '#') G[i][deg[i]++] = id[nx][ny];
			}
		}

		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++; }
		printf("%d\n", bfs());
	}
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值