【 OJ 】 HDOJ1026 BFS迷宫类问题 [ 25 ]

6 篇文章 0 订阅
3 篇文章 0 订阅
本文深入探讨了BFS(宽度优先搜索)算法在解决迷宫寻路问题中的应用,详细解析了如何利用队列实现BFS算法,通过具体实例展示了如何计算最短路径和路径方向,同时分享了在实际编码过程中遇到的内存限制问题及解决方案。
摘要由CSDN通过智能技术生成

这题  Memory Limit Exceeded  Memory Limit:32768 K本题在测试机上是35343K

这题如果想AC可以不用看本人的话了....因为超了内存,如果只是想了解思想,强烈推荐,感觉还是很不错滴~自卖自夸下

这题我用了visit 来记录了所有能到达结点的最短路径数(并不需要全做,只要到了目的地就可以retun结束)【想了一下这句话也是错了,原因很简单,虽然正常来说在队列中先被访问的一定是比较近的点,但是由于此题有n怪物,所以不一定是最近的,有可能目的地确实先被访问了,但它其实可以被松弛操作,意思它途径了n怪物点,因此它不是最近的路径,此时return了答案可能会不对】因此超了内存,开始思路有点乱,又想通过路径数来找找路径,又想用方向数组来指向

找路径,又想通过标记方向数组  -> 或者  <-  来找路径 后来用数字模拟方向

用dri 记录所有点到达map[0][0]的路径,逆推 1 ( -> )东 2 ( v )南 3 ( <- ) 西 4 ( ^ )北

重理思路:

之前说不需要做最短路径数是不对的,后来想了想,如果没有最短路径数那么就没办法确定是否做松弛操作,没法确定是否做松弛操作就没法确定是否修改指向.... 因此此题超内存.....当遇到目的地后直接return 剩下点都不用计算了,可能会....节省点内存
    1: 首先思想BFS用队列实现
    2 :  路径的最后输出又栈实现【可由数组代替】
    从(0,0)开始入队,读取到一个当前节点 p 就遍历他的四周所有可行点,如果【第一次发现这个节点】,将这个节点入队,然后标记路径长,为当前 p 节点路径+1,如果是 n,
    那么就再 +n (注 * 前面+1必做),如果遍历发现这个节点【不第一次发现这个节点】,那么就看能否松弛【松弛操作】,就是当前 p 节点+1是否小于这个节点目前的路径数,如果小于,那么这个节点还需要入队列,并且修改最短路径

如果不小于说明不用做  ---------以上BFS可以求出所有可到达节点的最短路径长度

(其实通过所有最短路径数也是可以逆推出路径的,从目的结点开始每次少1步就是,遇到n,减去(n+1)即可)

因为路径数为6的结点周围可能3个都是路径数为7的,但是7的周围肯定只有1个,因此可以逆推,但是不方便,

所以加上指向肯定会更加方便找
    因此本题思路来源于 BFS 两个串的最长共同子序列 (在数组中可以标记最短路径长度也可以给出路径的方向)如果有疑问可以看 算法导论的 BFS 例题讲解

    下面的所有操作其实是加在上述的求最短路径数步骤中的,读者自行体会
    依旧从(0,0)开始入队遍历周围每一个可行结点,如果第一次发现,将那个结点 标记下方向

【例如 当前点 p为 0,0发现向东走 0,1可以走,那么0,1点就标记为3(西)说明(0,1)向西走可以回到当前结点-这样从目的结点可以通过指向一路走到起始位置】

如果不是第一次,还是看是否能松弛,如果能松弛就修改指向
 直至整个地图遍历结束

//下面的函数用来输出BFS后地图的所有能到达点的最短路径

for (i = 0; i < N; i++) {
	for (j = 0; j < M; ++j) {
		if (visit[i][j] == IFMAX)
			cout << "X ";
		else
			cout << visit[i][j]<<" ";
	}
	cout << endl;
}//松弛后所有的地图信息已经清晰

//下面的函数用来输出地图的方向 1东 2 南 3西 4北 从 map[N-1][M-1]看方向到map[0][0]

for (i = 0; i < N; i++) {
	for (j = 0; j < M; ++j) {
		if (i == 0 && j == 0)
			cout << "S";
		else {
			if (dri[i][j] == 0)
				cout << "X";
			else
				cout << dri[i][j];
		}
}
cout << endl;
}

//下面是源代码 

# include <iostream>
# include <queue>
# include <stack>
#define IFMAX 200
using namespace std;
int N, M;//行 列
struct Path {
	int x;
	int y;
	int p;
}p;
int visit[102][102];//题意 最大100*100
int dri[102][102];
char map[102][102];
int d[4][2] = { { 0,1 },{ 1, 0 },{ 0, -1 },{ -1, 0 } };
int min(int a, int b) {
	return a < b ? a : b;
}
void PUSH(int dir_, queue<Path>&q) {
	if (map[p.x][p.y] == '.') {//正常步子
		if (p.p + 1<visit[p.x][p.y]) {//发现能被松弛就入栈
			visit[p.x][p.y] = ++p.p;
			q.push(p);
			dri[p.x][p.y] = dir_;
		}
	}
	else {//为n 
		if (visit[p.x][p.y] == IFMAX) {
			q.push(p);//第一次访问入队
			dri[p.x][p.y] = dir_;
		}
		visit[p.x][p.y] = min((p.p + 1 + (map[p.x][p.y] - '0')), visit[p.x][p.y]);//松弛操作是不是第一次入队都要做
		if ((p.p + 1 + (map[p.x][p.y] - '0')) < visit[p.x][p.y]) {//可以被松弛
			q.push(p);//可以松弛入队
			dri[p.x][p.y] = dir_;
		}
	}
}
void check(queue<Path>&q) {
	//地图信息  X .  n  
	for (int i = 1; i < 5; ++i) {//东 南 西 北
		p.x += d[i-1][0];
		p.y += d[i-1][1];
		if ((p.x >= 0 && p.x < N) && (0 <= p.y&&p.y < M) && map[p.x][p.y] != 'X') {//东
			PUSH((((i+1)%4)+1), q);
		}
		p = q.front();//回溯原来位置
	}
}
void BFS(void) {
	queue<Path> q;
	p.x = p.y = p.p = 0;
	q.push(p);
	while (!q.empty()) {
		p = q.front();
		p.p = visit[p.x][p.y];
		check(q);//检查四周,将可入队的入队
		q.pop();//将遍历过的出队
	}//遍历完所有地图
}
void TravelStack(stack<Path> &s) {
	int n, num = 0;
	Path pi;
	p = s.top();
	s.pop();
	while (!s.empty()) {
		pi = p;
		if (map[pi.x][pi.y] != '.') {
			n = map[pi.x][pi.y] - '0';
			for (int i = 0; i < n; ++i) {
				cout << ++num << "s:FIGHT AT(" << pi.x << ", " << pi.y << ")" << endl;
			}
		}
		p = s.top();
		s.pop();
		cout << ++num << "s:(" << pi.x << ", " << pi.y << ")->(" << p.x << ", " << p.y << ")" << endl;
	}
	cout << "FINISH" << endl;
}
void TrevealPath(void) {
	p.x = N - 1;
	p.y = M - 1;//终点标记起点位置
	p.p = visit[N - 1][M - 1];
	if (p.p == IFMAX) {//暗示了路径是不通的,所以呼叫上帝
		cout << "God please help our poor hero.\nFINISH" << endl;
		return;
	}
	stack<Path> s;
	s.push(p);//起始点入栈
	while (p.x != 0 || p.y != 0) {//终止位置不为(0,0)就循环
		for (int i = 1; i < 5; ++i) {
			if (dri[p.x][p.y] == i) {
				p.x += d[i - 1][0];
				p.y += d[i - 1][1];
				s.push(p);
				break;
			}
		}
	}
	cout << "It takes " << visit[N - 1][M - 1] << " seconds to reach the target position, let me show you the way." << endl;
	TravelStack(s);
}
int main(void) {
	while (cin >> N >> M) {// N 行 M 列
		int i, j;
		for (i = 0; i < N; i++) {
			for (j = 0; j < M; ++j) {
				cin >> map[i][j];
				visit[i][j] = IFMAX;
				dri[i][j] = 0;
			}
		}//录入地图信息
		visit[0][0] = 0;
		BFS();//BFS map信息
		TrevealPath();
	}
	system("pause");
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值