迪杰斯特拉算法的应用

1.问题描述

穷游?“穷”游?
Time Limit: 1000 MS Memory Limit: 10000 KB

Description

贫穷的小A有一个梦想,就是到t国去一次穷游,但现实是残酷的。小A所在的世界一共有n(n<=500)
个国家,国家与国家之间总共有E(E<=50000)条道路相连,第i个国家对于进入它的外国人都要收取Bi
的费用,而小A家住在s国,他必须通过这些道路在各个国家之间中转最终到达t国(除非他运气够好
可以直接从s国到达t国)。但是贫穷的小A只剩下M(M<=100)元家底了,因此他必须精打细算旅途的
费用,同时小A对于t国实在太向往了,因此他希望能够走最短的路尽快到达t国。这个问题难倒了小
A,现在他请你帮他算一算他到达t国的最短路径有多长。

Input

第一行输入T(T<=10)表示有T组数据。每组数据第一行输入n、E、s、t、M,分别表示小A所在世界
的国家数、国家之间的总道路数、小A的国籍、小A向往的国家以及小A的家底;接下来一行输入n个正
整数Bi,表示第i个国家收取的过路费(由于小A是s国人,因此s国不会收取,但t国会);接下来输
入E行每行三个正整数u(1<=u<=n)、v(1<=v<=n)、w,表示u国和v国之间存在着一条长度为w的无
向边(可能有重边)。输入保证最终结果不会使int溢出。

Output

输出T行正整数,第i行表示第i组数据小A花费不超过M元到达t国的最短路。若小A无法到达t国,输
出-1.

Sample Input

3
2 2 1 2 10
20 10
1 2 1
1 2 2
3 1 1 3 10
1 1 1
2 3 1
3 3 1 3 10
1 11 1
1 2 1
1 2 3
2 3 1

Sample Output

1
-1
-1

2.算法分析

此题中,目的是求从S国到T国的最短路径,显然应使用迪杰斯特拉算法。相应的收费可以看做边到边的权重,最终的花费为最短路径的权重和。若小于M,则小A可以到达,若大于M则小A无法到达

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

// 结构体表示图中的一条边
struct country {
	int v, w; // 顶点和权重
};

vector <country> world[505]; // 图的邻接表表示

// 结构体表示图中的一个顶点
struct vertex {
	int m, dis, v; // m: 剩余的金钱,dis: 距离,v: 顶点
};

// 自定义比较函数,用于优先队列的排序
struct mycompare {
	bool operator()(vertex a, vertex b) {
		return a.dis > b.dis; // 根据距离比较顶点
	}
};

priority_queue<vertex, vector<vertex>, mycompare> arr; // 用于Dijkstra算法的优先队列

int main() {
	int T, n, E, s, t, M, x, y, z;
	cin >> T; // 读取测试用例的数量

	int cost[505]; // 数组用于存储每个顶点的成本
	int distance[505][105]; // 二维数组用于存储最短距离
	bool flag[505][105]; // 二维数组用于跟踪访问过的顶点

	while (T--) { // 遍历每个测试用例
		memset(distance, -1, sizeof(distance)); // 将距离数组初始化为-1
		memset(flag, true, sizeof(flag)); // 将标记数组初始化为true

		cin >> n >> E >> s >> t >> M; // 读取顶点数、边数、起点、终点和初始金钱

		for (int i = 1; i <= n; i++)
			cin >> cost[i]; // 读取每个顶点的成本

		for (int i = 0; i < E; i++) {
			cin >> x >> y >> z; // 读取边的起点、终点和权重
			country temp;
			temp.v = y;
			temp.w = z;
			world[x].push_back(temp); // 将边添加到起点的邻接表中
			temp.v = x;
			world[y].push_back(temp); // 将边添加到终点的邻接表中(无向图)
		}

		vertex temp;
		temp.m = M; temp.dis = 0; temp.v = s; distance[temp.v][M] = 0;
		arr.push(temp); // 将起点加入优先队列

		while (!arr.empty()) {
			temp = arr.top(); // 取出距离最小的顶点
			arr.pop();

			int p = temp.v;
			if (!flag[p][temp.m] || temp.dis > distance[temp.v][temp.m]) continue; // 如果顶点已被访问过或距离更大,则跳过此次循环

			flag[p][temp.m] = false; // 标记顶点为已访问

			for (int i = 0; i < world[p].size(); i++) {
				int v = world[p][i].v;
				if (v == s) continue; // 如果顶点为起点,则跳过此次循环

				if (temp.m >= cost[v] && (distance[v][temp.m - cost[v]] == -1 || temp.dis + world[p][i].w < distance[v][temp.m - cost[v]])) {
					distance[v][temp.m - cost[v]] = temp.dis + world[p][i].w; // 更新距离数组

					vertex point;
					point.dis = temp.dis + world[p][i].w;
					point.m = temp.m - cost[v];
					point.v = v;
					arr.push(point); // 将新的顶点加入优先队列
				}
			}
		}

		int ans = 1000000;
		bool index = 0;

		for (int i = 0; i <= M; i++) {
			if (distance[t][i] != -1) {
				ans = min(ans, distance[t][i]); // 更新最短距离
				index = 1;
			}
		}

		if (!index)
			cout << -1 << endl;
		else
			cout << ans << endl;

		for (int i = 1; i <= n; i++)
			world[i].clear(); // 清空邻接表
	}
}

猜你喜欢

转载自blog.csdn.net/lyhizjj/article/details/130749526