题目
题目大意
给出城市的个数n,路的数目m,起点s和终点d。接下来以c1 c2 d cost的形式给出两座城市之间的距离和花费。要求计算从起点到终点的最短路径,如果有多条最短路径,则输出花费最小的最短路径。然后再输出最短路径的总距离和总花费。
思路
迪杰斯特拉求最短路径。dist数组记录最短路径的长度,visited数组标志节点是否被激活(无需temp数组)。要求输出最短路径,就需要一个数组来记录节点值,比较常见的做法就是构建一维数组path记录节点的前驱节点值,最后输出的时候递归回溯path数组即可。还需要minCost数组,记录每个节点的最小花费,便于在多个最短路径中选出最小花费的路径。
代码
#include <iostream>
#include <vector>
#include <climits>
using namespace std;
int n, m, s, d;
int g[501][501] = {0}; // 图
int cost[501][501] = {0}; // 记录花费
vector<int> dist; // 记录最短路径
vector<int> minCost; // 记录最小花费
vector<int> path; // 记录最短路径的前驱节点
void dijie() {
bool visited[501] = {false};
dist[s] = 0;
minCost[s] = 0;
for (int cnt = 0; cnt < n; cnt++) {
int now = -1; // 要激活的节点
int minDist = INT_MAX;
// 寻找未访问的节点中距离最小的节点
for (int i = 0; i < n; i++) {
if (!visited[i] && dist[i] < minDist) {
minDist = dist[i];
now = i;
}
}
if (now == -1) return; // 如果找不到,说明所有节点都已访问,退出
visited[now] = true; // 激活节点 now
// 更新从 now 到其他节点的距离和花费
for (int i = 0; i < n; i++) {
if (!visited[i] && g[now][i] < INT_MAX) {
if (dist[i] > dist[now] + g[now][i]) {
dist[i] = dist[now] + g[now][i];
minCost[i] = minCost[now] + cost[now][i];
path[i] = now; // 更新i的前驱节点
} else if (dist[i] == dist[now] + g[now][i]) {
if (minCost[i] > minCost[now] + cost[now][i]) {
minCost[i] = minCost[now] + cost[now][i];
path[i] = now;
} else if (minCost[i] == minCost[now] + cost[now][i]) {
path[i] = now;
}
}
}
}
}
}
void printPath(int node) {
if (node == s) {
cout << node << " ";
return;
} // 如果是起点,完成输出并返回
printPath(path[node]); // 回溯前驱节点
cout << node << " "; // 输出当前节点
} // 递归输出路径,回溯path数组
int main() {
cin >> n >> m >> s >> d;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
g[i][j] = (i == j) ? 0 : INT_MAX;
}
} // 初始化图
for (int i = 0; i < m; i++) {
int c1, c2, dist, weight;
cin >> c1 >> c2 >> dist >> weight;
g[c1][c2] = g[c2][c1] = dist;
cost[c1][c2] = cost[c2][c1] = weight;
} // 得到图和花费矩阵
dist.resize(n, INT_MAX);
minCost.resize(n, INT_MAX);
path.resize(n);
dijie();
printPath(d); // 输出从 s 到 d 的路径
cout << dist[d] << " " << minCost[d] << endl;
return 0;
}