1. 程式人生 > >存圖方法之鏈式前向星+BFS例項精講

存圖方法之鏈式前向星+BFS例項精講

存圖方法有很多,最暴力的方法就是開一個二維陣列

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]);
	}
}

那麼最高效的鏈式前向星存圖法來了,見到有一位博主講的很好

接下來的就是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;
}