存图方法之链式前向星+BFS实例精讲

训练题目链接(密码hpuacm): https://vjudge.net/contest/242570

存图方法有很多,最暴力的方法就是开一个二维数组

int maze[1000][1000]; //最多能大概5000 5000
int a, b, c;	// 一条从a到b的权值为c的边

while( cin >> a >> b >> c )
{
	maze[a][b] = c;
	maze[b][a] = c;

}

但是这样很浪费时间和空间。

于是另一种存图方法是邻接表法。  什么!!! 你还不知道什么是邻接表,戳这里:  点击此处跳转

邻接表法常用的是STL中的vector

vector<int> e[10000];	// edge	边
vector<int> v[10000];	// val	权值
int a, b, c;	// 一条从a到b的权值为c的边

while( cin >> a >> b >> c )
{
	e[a].push_back(b);
	v[a].push_back(c);
	e[b].push_back(a);
	v[b].push_back(c);
}
/* 遍历 */
for( int i=0; i<10000; i++ )
{
	for( int j=0; j<e[i].size(); j++ )
	{
		if( e[i][j] != 0 )
			printf("...\n", i, e[i][j], v[i][j]);
	}
}

那么最高效的链式前向星存图法来了,见到有一位博主讲的很好

链接:https://blog.csdn.net/acdreamers/article/details/16902023

 

接下来的就是BFS了。

BFS的核心就是将未访问过的状态压入队列,然后逐个访问,访问的同时又会加入新的状态,直到找到终点或者队列变为空。仔细想想会明白的,切忌不能糊里糊涂的就过去了,或者理解的不透彻,这样就会造成一种尴尬的情况,就是遇到新的题不会写,但是一看题解立马就懂了。(这句话不是我说的,是一个“大牛”说的。)

之前就写过一篇博客介绍了BFS,当然那次是参考挑战程序设计上的。有点不是很好理解,可以看一下之前的那篇文章,很多关键点都还是讲到了。链接戳这里

这一次来了一个更详细的讲解,废话少说,先上题目

                                                Rescue

 

Angel was caught by the MOLIGPY! He was put in prison by Moligpy. The prison is described as a N * M (N, M <= 200) matrix. There are WALLs, ROADs, and GUARDs in the prison. 

Angel's friends want to save Angel. Their task is: approach Angel. We assume that "approach Angel" is to get to the position where Angel stays. When there's a guard in the grid, we must kill him (or her?) to move into the grid. We assume that we moving up, down, right, left takes us 1 unit time, and killing a guard takes 1 unit time, too. And we are strong enough to kill all the guards. 

You have to calculate the minimal time to approach Angel. (We can move only UP, DOWN, LEFT and RIGHT, to the neighbor grid within bound, of course.) 

Input

First line contains two integers stand for N and M. 

Then N lines follows, every line has M characters. "." stands for road, "a" stands for Angel, and "r" stands for each of Angel's friend. 

Process to the end of the file. 

Output

For each test case, your program should output a single integer, standing for the minimal time needed. If such a number does no exist, you should output a line containing "Poor ANGEL has to stay in the prison all his life." 

Sample Input

7 8
#.#####.
#.a#..r.
#..#x...
..#..#.#
#...##..
.#......
........

Sample Output

13

题意就是输入一个n和m代表一个n行m列的迷宫矩阵。其中a为起点 r为终点。 遇到‘#’就不能继续前进了 ,遇到‘x’需要走两步才能过去,遇到‘.’ 只需一步。问最少需要走多少步?

上代码:

要点部分都注释了,稍微有点基础的都可以看明白,有不懂的评论区解答。

#include <bits/stdc++.h>
using namespace std;
 
const int MAXN = 200 + 10;
char str[MAXN][MAXN]; 	// 读入字符串
int vis[MAXN][MAXN];	// 标记
int n, m;
 
struct node{
	int x, y;
	int step;
	friend bool operator <( node a, node b )	// 重载函数 排序,定义在内部,用在下面提到的优先队列
	{
		return a.step> b.step;
	}
};
/*	定义在结构体外部的重载函数, 排序
bool operator < ( node a, node b )
{
	return a.step > b.step;
}	*/
int d[4][2] = { 1, 0, -1, 0, 0, 1, 0, -1 };  // 方向数组
void BFS( int x1, int y1, int x2, int y2 )
{
	memset( vis, 0, sizeof(vis) );	// 初始化vis标记数组
	//priority_queue<node> q; 	// 定义为普通的队列也可以
	queue<node> q;
	node e1, e2;
	e1.x = x1, e1.y = y1, e1.step = 0;
	q.push(e1);		// 把初始状态放入优先队列
	vis[x1][y1] = 1;	// 标记已经走过的点
	int ans = -1;	// 记录结果
	while( !q.empty() )
	{
		e1 = q.front();	// 取出队列内的步数最少的点
		q.pop();		// 该状态遍历过了
		if( e1.x == x2 && e1.y == y2 )	// 如果到达终点,终止掉
		{
			ans = e1.step;
			break;
		}
		for( int i=0; i<4; i++ )
		{
			e2.x = e1.x + d[i][0];
			e2.y = e1.y + d[i][1];
			if( e2.x < 0 || e2.x >= n || e2.y < 0 || e2.y >= m ) continue;
			if( vis[e2.x][e2.y] == 1 ) continue;
			if( str[e2.x][e2.y] == '#' ) continue;
			if( str[e2.x][e2.y] == '.' || str[e2.x][e2.y] == 'r' )
				e2.step = e1.step + 1;
			else if( str[e2.x][e2.y] == 'x' )
				e2.step = e1.step + 2;
			/* 第二种写法
			if(str[e2.x][e2.y] == 'x')
				e2.step = e1.step + 2;
			else
				e2.step = e1.step + 1;
			*/
			q.push(e2);		// 压入队列
			vis[e2.x][e2.y] = 1;	// 走过的标记为1
		}
	}
	if( ans == -1 )
		puts("Poor ANGEL has to stay in the prison all his life.");
	else
		printf("%d\n", ans );
 
}
int main()
{
	int edx, edy, stx, sty;		// 结束点和起点
	while( scanf("%d %d", &n, &m ) != EOF )
	{
		for( int i=0; i<n; i++ )
			scanf("%s", str[i] );
		for( int i=0; i<n; i++ )
		{
			for( int j=0; j<m; j++ )
			{                            // 找到起点和终点
				if( str[i][j] == 'a' )
					stx = i, sty = j;
				if( str[i][j] == 'r' )
					edx = i, edy = j;
			}
		}
 
 
		BFS(stx, sty, edx, edy);
	}
 
 
 
 
	return 0;
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值