PTA甲級考試真題練習3——1003 Emergency

  • 題目:
    在這裏插入圖片描述
  • 思路:
    該題就是求圖的最短路徑,首先注意求得是最短路徑的數目而不是最短路徑的長度,然後還有一個救援隊的問題。下面使用兩種方法解決該問題:

(1)dijkstra算法:
基本思想:每次找到離源點(如1號節點)最近的一個頂點,然後以該頂點爲中心進行擴展,最終得到源點到其餘所有點的最短路徑。

  • 基本步驟:
    1,設置標記數組book[]:將所有的頂點分爲兩部分,已知最短路徑的頂點集合P和未知最短路徑的頂點集合Q,很顯然最開始集合P只有源點一個頂點。book[i]爲1表示在集合P中;
    2,設置最短路徑數組dst[]並不斷更新:初始狀態下,dst[i]=edge[s]i,很顯然此時dst[s]=0,book[s]=1.此時,在集合Q中可選擇一個離源點s最近的頂點u加入到P中。並依據以u爲新的中心點,對每一條邊進行鬆弛操作(鬆弛是指由頂點s–>j的途中可以經過點u,並令dst[j]=min(dst[j],dst[u]+edge[u][j])),並令book[u]=1;
    3,在集合Q中再次選擇一個離源點s最近的頂點v加入到P中。並依據v爲新的中心點,對每一條邊進行鬆弛操作(即dst[j]=min(dst[j],dst[v]+edge[v][j])),並令book[v]=1;
    4,重複3,直至集合Q爲空。
  • 代碼:
#include <iostream>
#include <cstring>
#define nmax 1000
#define inf 999999
using namespace std;
int minpath[nmax],maxperson[nmax], n, m, edge[nmax][nmax],book[nmax],person[nmax];   //最短路徑,節點數,邊數,終點,鄰接矩陣,集合標記,
int src, dest;   //源和目的
int ipath[nmax];

void Dijkstra()
{
	//找除了源點外的n-1個點
	for (int i = 0; i < n; i++)
	{
		int min = inf;
		int u = 0;
		for (int j = 0; j < n; j++)
		{
			if (book[j] == 0 && minpath[j] < min)
			{
				min = minpath[j];
				u = j;
			}
		}
		book[u] = 1;
		//更新最短距離
		for (int j = 0; j < n; j++)
		{
			if (book[j] == 0 && minpath[u] + edge[u][j] < minpath[j])
			{
				minpath[j] = minpath[u] + edge[u][j];
				ipath[j] = ipath[u];
				maxperson[j] = maxperson[u] + person[j];
			}
			else if (book[j] == 0 && minpath[u] + edge[u][j] == minpath[j])
			{
				ipath[j] += ipath[u];
				if (maxperson[j] < maxperson[u] + person[j])
					maxperson[j] = maxperson[u] + person[j];
			}
		}
	}
}
int main()
{
	int a, b;
	int count = 0;
	cin >> n >> m >> src >> dest;
	for (int i = 0; i < n; i++)
	{
		cin >> person[i];
	}
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < n; j++)
		{
			edge[i][j] = inf;
		}
	}
	for (int i = 0; i < m; i++)
	{
		cin >> a >> b;
		cin >> edge[a][b];
		edge[b][a] = edge[a][b];
	}
	memset(book, 0, sizeof(book));
	for (int i = 0; i < n; i++)
	{
		ipath[i] = 1;
		minpath[i] = edge[src][i];
		if(minpath[i]!=inf&&i!=src)
			maxperson[i] = person[src] + person[i];
	}	
	minpath[src] = 0;
	maxperson[src] = person[src];
	book[src] = 1;
	Dijkstra();
	cout << ipath[dest] <<" "<<maxperson[dest];
	return 0;
}

(2)深度或廣度優先算法:
從起點開始訪問所有深度遍歷路徑或廣度優先路徑,則到達終點節點的路徑有多條,取其中路徑權值最短的一條則爲最短路徑。
和上面dijkstra相比較功能撿漏但是更加容易實現

#include <iostream>
#include <cstring>
#define nmax 1000
#define inf 999999
using namespace std;
int minpath = inf, maxperson = 0, n, m, edge[nmax][nmax], mark[nmax], person[nmax];   //最短路徑,節點數,邊數,終點,鄰接矩陣,節點訪問標記
int src, dest;   //源和目的
int path = 1;

void dfs(int cur, int dst, int num)
{
	if (dst > minpath) return;   //如果距離已經大於最小路徑了直接返回
	if (cur == dest) {  //如果到達目的地
		if (dst < minpath) {
			minpath = dst;
			maxperson = num;
			path = 1;
		}
		else if (dst == minpath) {
			if(num > maxperson)
				maxperson = num;
			path += 1;
		}
		return;
	}
	for (int i = 0; i < n; i++)
	{
		if (edge[cur][i] != inf && edge[cur][i] != 0 && mark[i] == 0)
		{
			mark[i] = 1;
			dfs(i, dst + edge[cur][i], num + person[i]);
			mark[i] = 0; //遞歸返回要將mark置0
		}
	}
}
int main()
{
	int a, b;
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < n; j++)
		{
			edge[i][j] = inf;
		}
		edge[i][i] = 0;
	}
	cin >> n >> m >> src >> dest;
	for (int i = 0; i < n; i++)
	{
		cin >> person[i];
	}
	for (int i = 0; i < m; i++)
	{
		cin >> a >> b;
		cin >> edge[a][b];
		edge[b][a] = edge[a][b];
	}
	memset(mark, 0, sizeof(mark));
	mark[src] = 1;
	dfs(src, 0, person[src]);
	cout << path << " " << maxperson;
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章