【图论】POJ-3026 Borg Maze

本文介绍了一道关于迷宫搜索的编程题,重点在于如何利用广度优先搜索(BFS)和Prim算法找到从起点到终点的最低成本路径。文章详细记录了解题过程中的难点及解决方案。

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

这段时间要沉迷刷题一段时间了,就让优快云陪我一起吧!

一、题目大意

博格是一个非常强大的种族,它来自银河系的三角洲象限。博格集体是用来描述博格文明群体意识的术语。每个博格人都通过复杂的子空间网络与集体联系,确保每个成员得到持续的监督和指导。

你的任务是帮助博格通过开发一个程序,帮助博格估计扫描迷宫的最低成本,以吸收隐藏在迷宫中的外星人,通过向上下左右移动。棘手的是,搜索的开始是由100多个人组成的。每当外星人被同化时,或者在搜索开始时,该群体可能会分成两组或更多组(但他们的意识仍然是集体的)。搜索迷宫的成本被定义为搜索中涉及的所有组所覆盖的总距离。也就是说,如果原始组走五步,则分成两组,每组步行三步,总距离为11 = 5 + 3 + 3。

题目输入的第一行,是一个整数N,代表测试样例数。每个测试样例的第一行是两个整数M,N,表示地图为NM (注意,不是我写反了,就是这样的!!).然后接下来就是NM的地图,其中’ '表示开放区域,'A’表示外星人,‘S’表示搜索开始的地方,’#'表示墙。

对于每个测试用例,输出一行包含成功搜索迷宫的最低成本,不留外星人。

二、题目思路以及AC代码

兄弟,看到这,我只能说你赚到了。一会你就知道我为什么这么说了,因为这道题的坑,我可以说几乎踩了个遍(哭死)。

首先,看到这道题,第一反应是bfs的题,但又仔细一看,不对!会分叉!然后再进行分析,发现其实分叉的目的就是要覆盖所有的可能路径,然后找一条最小的,这也就是最小生成树问题了,思路很明确,以为20分钟就可以写出来。呵,下面就是我的踩坑时间。

由于是最小生成树,就要明确顶点和边的权值,在本题中,顶点就是图中’S’和’A’的位置,而边自然就是他们相互之间需要达到的距离了(这里用bfs求解)。好了,思路明确那就写吧!

第一遍,很快bfs和Prim全部写完,提交!TLE,好吧,看来不能那么简单的bfs,那就优化一下,在O(n)的复杂度内求解(i, j)到所有顶点的距离,这样应该就可以了吧!

第二遍,又没过多长时间,新版bfs求出来了,提交!WA,终于不是TLE了,还有些许欣慰,WA嘛,我自以为思路又没错,肯定是爆int了吧,也没怎么仔细考虑,一想MST确实可能爆int,那就改吧!

第三遍,这就很快了,改int为LL,提交!还是WA?思路错了吗,没有啊,又琢磨了好长时间无果之后,我决定去寻求帮助,去看了一下discuss,发现了一个问题。
在这里插入图片描述
好了,这么明显也不需要我多说什么了,匿了。

下面给出AC代码:

bfs + Prim:

#include <iostream>
#include <queue>
#include <vector>
#define INF 10000000
#define MAXN 55
using namespace std;

struct Point {
	int x, y;
	Point(int a, int b) {
		x = a;
		y = b;
	}
};

typedef long long ll;
typedef pair<Point, ll> pp;

int dx[4] = { 0, 0, -1, 1 };
int dy[4] = { -1, 1, 0, 0 };

int flag[MAXN][MAXN];			// the identifier of node
char map[MAXN][MAXN];			// start from (1, 1)
int N, M;						// map is N * M Matrix
bool vis[MAXN][MAXN];

// For Prim
ll edges[110][110];			
ll dist[110];
bool visited[110];

void init() {
	for (int i = 0; i < MAXN; i++) {
		for (int j = 0; j < MAXN; j++) {
			flag[i][j] = 0;
			vis[i][j] = false;
			map[i][j] = '\0';
		}
	}
	for (int i = 0; i < 110; i++) {
		for (int j = 0; j < 110; j++) {
			if (i == j) edges[i][j] = 0;
			else edges[i][j] = INF;
		}
		dist[i] = INF;
		visited[i] = false;
	}
}

void bfs(int sx, int sy) {
	// initialization
	for (int i = 0; i < MAXN; i++) {
		for (int j = 0; j < MAXN; j++) {
			vis[i][j] = false;
		}
	}

	queue<pp> q;
	while (!q.empty())q.pop();
	Point start(sx, sy);
	q.push(make_pair(start, 0));
	vis[sx][sy] = true;
	while (!q.empty()) {
		pp p = q.front();	q.pop();

		int xx = p.first.x;
		int yy = p.first.y;
		ll pace = p.second;

		if (flag[xx][yy]) {
			int u = flag[sx][sy];
			int v = flag[xx][yy];

			edges[u][v] = edges[v][u] = pace;
		}

		for (int i = 0; i < 4; i++) {
			int nx = xx + dx[i];
			int ny = yy + dy[i];

			if (vis[nx][ny]) continue;
			if (nx <= 0 || nx > N || ny <= 0 || ny > M || map[nx][ny] == '#') continue;
			
			vis[nx][ny] = true;
			q.push(make_pair(Point(nx, ny), pace + 1));
		}
	}
}

ll Prim(int num) {
	// initialization
	for (int i = 0; i < 110; i++) {
		dist[i] = INF;
		visited[i] = false;
	}

	ll ans = 0;
	dist[1] = 0;
	for (int i = 1; i <= num; i++) {
		ll min = INF;
		int x = -1;
		for (int j = 1; j <= num; j++) {
			if (!visited[j] && dist[j] < min) {
				min = dist[x = j];
			}
		}
		
		if (x == -1) break;
		visited[x] = true;
		ans += min;

		for (int j = 1; j <= num; j++) {
			if (!visited[j] && dist[j] > edges[x][j]) {
				dist[j] = edges[x][j];
			}
		}
	}

	return ans;
}

int main()
{
	int T;				
	scanf("%d", &T);			// T <= 50

	while (T--) {
		init();

		scanf("%d%d", &M, &N);		// N * M	N, M <= 50

		char c;
		while ((c = getchar()) != '\n') {
			
		}
		int sx, sy;
		int idx = 1;				// idx <= 100
		for (int i = 1; i <= N; i++) {				// start from (1, 1)
			for (int j = 1; j <= M; j++) {
				map[i][j] = getchar();
				if (map[i][j] == 'S' || map[i][j] == 'A') {
					flag[i][j] = idx++;
				}
			}
			getchar();
		}

		int size = 0;				// size <= 100, same as idx
		for (int i = 1; i <= N; i++) {
			for (int j = 1; j <= M; j++) {
				if (flag[i][j]) {
					bfs(i, j);
					size++;
				}
			}
		}

		printf("%lld\n", Prim(size));
	}

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值