题目内容:
解题思路:
本题整体是一个运用 dijkstra算法 的题目,然后在传统dijkstra算法里增加了 最多救援队 和 最短路径的条数 的要求,接下来我会根据本题简单讲解一下 dijkstra算法 和在这基础上如何找到 在路径最短的基础上救援队最多 的路径,以及如何寻找 最短路径的条数。
dijkstra算法:
Dijkstra算法的核心思想是贪心策略,它逐步扩展最短路径树,直到包含所有顶点。算法维护一个集合,该集合包含从源点到集合中每个顶点的最短路径。
首先针对本题输入样例,对于每个点需要记录三个数据,是否走过,到达该点的最短距离,父节点。
步骤一、选择目前所能到达的最短距离的点,记录该点已经走过。
步骤二、搜寻该点所能到达的所有点,比较新距离和原距离,若新距离更小则更新距离,以及父节点。
题目样例过于简单,可能并不能看出 Dijkstra算法 的操作步骤,下面我展示一个稍微复杂一点的案例。
故可以得到最短路径为 1 --> 3 --> 6 --> 8 --> 9, 最短路径长度为24。
然后对于本题还需要添加两个参数,救援队人数,这个参数可以和距离同时进行更新;第二个参数是 最短路径的条数,可以定义一个标记量,在遇到距离相同时,进行更新,同时比较救援队人数选择它的父节点。
C++代码展示:
#include <bits/stdc++.h>
using namespace std;
const int N = 505; // 定义最大节点数
const int INF = 1000000; // 定义无穷大
int n, m, s, d; // 定义变量n(节点数),m(边数),s(起点),d(终点)
int saveteam[N], st[N]; // 定义数组saveteam用于存储每个节点的救援队人数,st用于存储到达每个节点的最多救援队人数
int mm[N][N]; // 定义邻接矩阵mm,用于存储图的权重
int vis[N] = {0}; // 定义数组vis,用于标记节点是否已经被访问过
int dis[N]; // 定义数组dis,用于存储从起点到每个节点的最短距离
int father[N]; // 定义数组father,用于存储路径树,即每个节点的父节点
int num[N] = {0}; // 定义数组num,用于存储到达每个节点的路径数量
void Dijkstra() {
fill(dis, dis + N, INF); // 初始化所有节点的距离为无穷大
dis[s] = 0; // 起点到自身的距离为0
num[s] = 1; // 起点的路径数量为1
for (int i = 0; i < n; i++) {
int node = -1, minn = INF; // 定义变量node用于存储当前最近的节点,minn用于存储最小距离
for (int j = 0; j < n; j++) {
if (vis[j] == 0 && minn > dis[j]) {
node = j; // 更新最近的节点
minn = dis[j]; // 更新最小距离
}
}
vis[node] = 1; // 标记当前节点为已访问
if (node == -1) return; // 如果没有找到未访问的节点,则退出
for (int j = 0; j < n; j++) {
if (vis[j] == 0) { // 遍历未访问的节点
if (mm[node][j] != -1 && dis[node] + mm[node][j] < dis[j]) {
father[j] = node; // 更新父节点
dis[j] = dis[node] + mm[node][j]; // 更新距离
st[j] = st[node] + saveteam[j]; // 更新到达该节点的最多救援队人数
num[j] = num[node]; // 更新路径数量
} else if (mm[node][j] != -1 && dis[node] + mm[node][j] == dis[j]) {
if (st[j] < st[node] + saveteam[j]) {
father[j] = node; // 更新父节点
st[j] = st[node] + saveteam[j]; // 更新到达该节点的最多救援队人数
}
num[j] += num[node]; // 更新路径数量
}
}
}
}
}
int main() {
vector<int> sc; // 定义一个向量,用于存储路径
cin >> n >> m >> s >> d; // 读取节点数、边数、起点和终点
for (int i = 0; i < n; i++) {
cin >> saveteam[i];
father[i] = i; // 初始化每个节点的父节点为自己
st[i] = saveteam[i]; // 初始化到达每个节点的救援队人数
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
mm[i][j] = -1; // 初始化邻接矩阵
}
}
for (int i = 0; i < m; i++) {
int a, b, l;
cin >> a >> b >> l;
mm[a][b] = l; // 更新邻接矩阵
mm[b][a] = l; // 无向图,所以需要更新两个方向的权重
}
Dijkstra(); // 调用Dijkstra算法
cout << num[d] << " " << st[d] << "\n"; // 输出到达终点的路径数量和最多救援队人数
int p = d; // 定义变量p,用于存储当前节点
while (1) {
if (p == father[p]) {
sc.push_back(p); // 将当前节点加入路径
break; // 如果当前节点是起点,则退出循环
}
sc.push_back(p); // 将当前节点加入路径
p = father[p]; // 向上追溯父节点
}
for (int i = sc.size() - 1; i > 0; i--) { // 逆序输出路径
cout << sc[i] << " ";
}
cout << sc[0] << "\n";
return 0;
}