算法筆記---【PAT A1003 】Emergency

題目鏈接:【PAT A1003 】Emergency

題目描述

As an emergency rescue team leader of a city, you are given a special map of your country. The map shows several scattered cities connected by some roads. Amount of rescue teams in each city and the length of each road between any pair of cities are marked on the map. When there is an emergency call to you from some other city, your job is to lead your men to the place as quickly as possible, and at the mean time, call up as many hands on the way as possible.

Input Specification:

Each input file contains one test case. For each test case, the first line contains 4 positive integers: N (≤500) - the number of cities (and the cities are numbered from 0 to N−1), M - the number of roads, C1and C2 - the cities that you are currently in and that you must save, respectively. The next line contains N integers, where the i-th integer is the number of rescue teams in the i-th city. Then M lines follow, each describes a road with three integers c1, c​2 and L, which are the pair of cities connected by a road and the length of that road, respectively. It is guaranteed that there exists at least one path from C1 to C​2.

Output Specification:

For each test case, print in one line two numbers: the number of different shortest paths between C1 and C2 , and the maximum amount of rescue teams you can possibly gather. All the numbers in a line must be separated by exactly one space, and there is no extra space allowed at the end of a line.

題目大意:

給出N個城市,M條無向邊。每個城市中都有一定數目的救援小組,所有邊的邊權已知。現在給出起點和終點,求從起點到終點的最短路徑條數及最短路徑上的救援小組數目之和。
如果有多條最短路徑,則輸出數目之和最大的。

樣例:

輸入:
5 6 0 2// 表示5個結點,6條邊,起始點爲0,目的點爲2
1 2 1 5 3//表示每個結點的權
0 1 1//表示 0 1 之前的邊權爲 1
0 2 2
0 3 1
1 2 1
2 4 1
3 4 1
輸出:
2 4//表示起點到終點兩條最短路徑,點權的最大值爲4

運行結果:

在這裏插入圖片描述
解題思路:
可以直接使用Dijkstra算法求解,但是很容易出錯。
書中推薦使用Dijkstra算法+ DFS 來求解
具體過程:
1、使用Dijkstra算法求出起始結點到每個節點的最短路徑和最短路徑上的結點的父節點。
2、使用dfs算法遍歷最短路徑生成的樹。
這樣做的好處是,將邊權值和點權值的判斷分開處理。
而且給出最短路徑上結點的父節點數組,就可以很方便的求解其他限制條件下的路徑。

書中給出的算法是使用鄰接矩陣來實現Dijkstra算法,博主使用的是鄰接表來實現,且使用優先隊列來獲取當前結點的鄰接邊的最小值。
如果Dijkstra算法使用優先隊列不是很清楚如何編寫代碼,可以看博主算法筆記—Dijkstra算法的實現。

下面爲AC代碼:

#include<iostream>
#include<queue>
#include<vector>
#include<map>
#include<algorithm>
#include<limits.h>
using namespace std;

const int dij_max_v = 1010;

int node_weight[dij_max_v] = { 0 };//表示每個城市的點權
int num[dij_max_v] = {0};//表示最短路徑的條數
vector<int> path, temp_path;//表示最短路徑
int optionVal = 0;
vector<int> parent[dij_max_v];//表示當前結點的父結點

struct DijNode
{
	int id;//當前結點id
	int weight;//到該結點的權值,即邊權
};

struct MinWeightCompare//小頂堆
{
	bool operator() (const DijNode node1, const DijNode node2) const{
		return node1.weight > node2.weight;
	}
};


//************************************
// Method:    dijkstra 返回單源最短路徑
// FullName:  dijkstra
// Access:    public 
// Returns:   void
// Qualifier:
// Parameter: vector<DijNode> matrix[] 給定的圖
// Parameter: DijNode start 初始結點
// Parameter: bool * visited 表示當前結點是否被訪問
// Parameter: vector<int> parent[] 表示當前到結點的上一個結點
// Parameter: map<int,int> & distance 表示初始結點到當前結點的最短路徑
//************************************
void dijkstra(vector<DijNode> matrix[], DijNode start, bool* visited, vector<int> parent[], map<int, int>& distance) {
	
	priority_queue<DijNode, vector<DijNode>, MinWeightCompare> p_queue;//BFS 隊列
	//該優先隊列中的元素表示到當前結點的最短路徑。
	num[start.id] = 1;//表示當前結點的最短路徑只有1條
	p_queue.push(start);

	while (!p_queue.empty())
	{
		DijNode top_node = p_queue.top();
		p_queue.pop();
		visited[top_node.id] = true;//標記當前結點已被訪問
		for (int i = 0;i < matrix[top_node.id].size();i++)
		{
			DijNode next_node = matrix[top_node.id][i];
			//當前結點沒有被訪問,且以 top_node 爲中介點可以使得 next_node 更優
			if (visited[next_node.id] == false)
			{
				if (distance[top_node.id] + next_node.weight < distance[next_node.id])
				{
					distance[next_node.id] = distance[top_node.id] + next_node.weight;
					DijNode node;
					node.id = next_node.id;
					node.weight = distance[next_node.id];//更新當前的最短距離
					num[next_node.id] = num[top_node.id];//更新當前路徑的條數
					p_queue.push(node);//將所遍歷的結點加入到隊列中
					parent[next_node.id].clear();//清空parent 當前結點數組
					parent[next_node.id].push_back(top_node.id);//更新所遍歷結點的父結點
				}else if (distance[top_node.id] + next_node.weight == distance[next_node.id])//表示最短距離相等
				{
					parent[next_node.id].push_back(top_node.id);//更新所遍歷結點的父結點
					num[next_node.id] += num[top_node.id];//更新最短路徑的條數
				}
				
			}
		}
	}
}

//打印最短路徑所經過的結點
void print_path(vector<int>& path) {
	for (int i = path.size() - 1;i >= 0;i--)
	{
		cout << path[i] << " ";
	}
}

//求出最優解
void dfs(int start_node, int end_node) {

	if (end_node == start_node)
	{
		temp_path.push_back(end_node);
		int value = 0;
		//計算點權
		for (vector<int>::iterator it = temp_path.begin(); it != temp_path.end(); it++)
		{
			value += node_weight[(*it)];
		}
		if (value > optionVal)
		{
			optionVal = value;
			path = temp_path;
		}
		temp_path.pop_back();
		return;
	}
	temp_path.push_back(end_node);
	for (int i = 0;i < parent[end_node].size();i++)
	{
		dfs(start_node, parent[end_node][i]);
	}
	temp_path.pop_back();
}


int main() {

	int n, m, st, ed;//表示頂點個數,邊數,初始結點,結束結點
	cin >> n >> m >> st >> ed;
	for (int i = 0;i < n;i++)
	{
		cin >> node_weight[i];
	}

	int form_node, to_node, weight;//表示起始結點,終止結點,權值
	vector<DijNode> matrix[dij_max_v];//用來存放圖
	DijNode node;

	map<int, int> distance;//記錄起始結點到其他結點的最短路徑
	distance[st] = 0;//自己到自己的距離爲 0 
	for (int i = 0;i < m;i++)
	{
		cin >> form_node >> to_node >> weight;
		//注意是無向圖
		node.id = to_node;
		node.weight = weight;
		matrix[form_node].push_back(node);

		node.id = form_node;
		node.weight = weight;
		matrix[to_node].push_back(node);

		if (to_node != st)
		{
			distance[to_node] = INT_MAX;//表示除了起始結點,剩下的皆爲不可達
		}
	}
	
	bool visited[dij_max_v];
	fill(visited, visited + dij_max_v, false);
	node.id = st;//起始結點的id
	node.weight = 0;//起始結點距離自己的距離
	
	parent[st].push_back(-1);//初始化起始結點的父結點
	dijkstra(matrix, node, visited, parent, distance);
	dfs(st, ed);
	cout << num[ed] << " " <<optionVal;
	system("pause");
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章