最短路径求解(Dijkstra算法)

1. 问题描述(问题来自于PAT A 1003Emergency)

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, C​1​​ and C​2​​ - 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 c​1​​, 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 C​1​​ to C​2​​.

Output Specification:
For each test case, print in one line two numbers: the number of different shortest paths between C​1​​ and C​2​​, 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.

Sample Input:

5 6 0 2
1 2 1 5 3
0 1 1
0 2 2
0 3 1
1 2 1
2 4 1
3 4 1

Sample Output:

2 4

题目的大概意思:

给出N个城市,M条无向边。每个城市都有一定数目的救援小组,所有边的边权已知,现在给出起点与终点,求从起点到终点的最短路径条数以及最短路径上的救援小组数目之和,如果存在多条最短路径那么输出数目之和最大的

样例解释:如下图所示,每个点的括号中是点权,每条边上标有边权,从v0到v2的最短路径是2,共有两条:v0->v2以及v0->v1->v2,这两条路径上的点权之和分别是2和4因此选择较大者

2. 思路分析:

① 分析题目我们可以知道题目的核心还是在求解最短路径,所以我们可以使用Dijkstra算法来求解最短路径,但是与之前纯粹使用Dijkstra算法不同的是之前的知识求解从源点到其余顶点的最短路径但是这里发生了一点变化,就是最短路径可能存在多条,而且增加可一个额外的条件就是需要求解出最短路径上点权最大的那条最短路径的顶点数目,这也就是Dijskstra算法的一个变体

② 求解最短路径上最大点权之和我们可以声明一个数组w[],w数组表示的意思是从源点s到v可以得到的最大点权之和,初始为0,因为需要求解出最短路径的条数那么我们需要声明一个另外的数组num,num数组表示的意思是从源点s到达顶点u的最短路径条数,初始化的时候之和num[s] = 1其余num[v]为0,接下来就需要在更新d[v]的时候更新这两个数组

③ 与之前求解源点到其余顶点的最短路径的过程很类似,不同的是增加了两个数组来记录我们需要求解的相关值,然后在循环中增加一个判断看当前的中间顶点u是否能够使得s到顶点v的距离是相等的,相等的话那么更新到达num[v]的最短路径的条数,而且判断是否点权增加了,假如更加了那么更新w[v]的点权

④ 这样我们就解决了上面的问题了,核心还是Dijkstra算法然后再加上一些数组来辅助解决即可

3. 具体的代码如下:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm> 
using namespace std;
const int maxSize = 510;
const int INF = 1000000000;
int n, m, st, ed, G[maxSize][maxSize], weight[maxSize];
int d[maxSize], w[maxSize], num[maxSize];
bool vis[maxSize] = {false};
int pre[maxSize];
void Dijstra(int s){
	fill(d, d + maxSize, INF);
	memset(num, 0, sizeof(num));
	memset(w, 0, sizeof(w));
	d[s] = 0;
	w[s] = weight[s];
	num[s] = 1;
	for(int i = 0; i < n; i++){
		int u = -1, min = INF;
		for(int j = 0; j < n; j++){
			if(vis[j] == false && d[j] < min){
				u = j;
				min = d[j];
			}
		}
		if(u == -1) return;
		vis[u] = true;
		for(int v = 0; v < n; v++){
			if(vis[v] == false && G[u][v] != INF && d[u] + G[u][v] < d[v]){
				d[v] = d[u] + G[u][v];
				w[v] = w[u] + weight[v];
				num[v] = num[u];	
			}else if(d[u] + G[u][v] == d[v]){
				if(w[u] + weight[v] > w[v]){
					w[v] = w[u] + weight[v];
				}
				num[v] += num[u];
			}
		}
	}	
}

int main(void){
	cin >> n >> m >> st >> ed;
	for(int i = 0; i < n; ++i){
		cin >> weight[i];
	}
	int u, v, t;
	fill(G[0], G[0] + maxSize * maxSize, INF);
	for(int i = 0; i < m; i++){
		cin >> u >> v >> t;
		G[u][v] = G[v][u] = t; 
	}
	Dijstra(st);
	cout << num[ed] << " " << w[ed];
}

猜你喜欢

转载自blog.csdn.net/qq_39445165/article/details/93225217