题目大意
现有N个节点和M条边的图,给定起点和终点,找出一条距离最短的路径和一条耗时最少的路径。其规则如下:
- 1、对于有多条最短路径,找到耗时最短的
- 2、对于有多条耗时最短路径找到岔路口(顶点)最少的
- 3、如果最短路径和耗时最少路径一样,合并输出。
算法思路
此题为比较常规的最短路径问题,使用两次迪杰斯特拉算法就可以进行求解,第一次求解距离最短的路径,使用距离作为第一标尺,耗时作为第二标尺。第二次使用耗时最短的路径,使用耗时作为第一标尺,路径上的节点数目作为第二标尺。迪杰斯特拉算法的过程不再这里赘述。对于两条最短路径是否完全一样的判断方法就是直接比较前驱数组,从终点开始一直往前走,遇到不同的说明是不同路径。
注意点
- 1、测试点4错误的情况极有可能是在编写第二次迪杰斯特拉算法的时候复制了之前的代码导致有些变量的名字没有更改过来。
- 2、第二次使用迪杰斯特拉算法求解的时候得初始化visited数组。
提交结果
AC代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 501;
int N, M;// 顶点和边的数目
int Start, End;// 起点和终点
bool visited[maxn];// 访问标记数组
// 距离最短
int L[maxn][maxn];// 每一对节点间的长度
int disL[maxn]; // 每一个节点到起点的最短距离
int preL[maxn]; // 求解最短距离路径中每一个节点的前驱节点
// 耗时最短
int T[maxn][maxn];// 每一对节点间的时间
int disT[maxn]; // 每一个节点到起点的最短耗时
int preT[maxn]; // 求解最短耗时路径中每一个节点的前驱节点
int num[maxn]; // 求解最短耗时路径中每一个节点到起点的最短节点数目
void findShortestPath(int start) {
fill(disL, disL + maxn, 0x3fffffff);
fill(disT, disT + maxn, 0x3fffffff);
disL[start] = 0;
disT[start] = 0;
for (int i = 0; i < N; ++i) {
int minD = 0x3fffffff;
int minIndex = -1;
for (int j = 0; j < N; ++j) {
if (!visited[j] && disL[j] < minD) {
minD = disL[j];
minIndex = j;
}
}
// 剩下的节点不连通
if (minIndex == -1) return;
visited[minIndex] = true;
// 根据minIndex更新disL数组
for (int j = 0; j < N; ++j) {
if (!visited[j] && L[minIndex][j] != 0) {
if (disL[j] > disL[minIndex] + L[minIndex][j]) {
disL[j] = disL[minIndex] + L[minIndex][j];
disT[j] = disT[minIndex] + T[minIndex][j];
preL[j] = minIndex;
} else if (disL[j] == disL[minIndex] + L[minIndex][j] && disT[j] > disT[minIndex] + T[minIndex][j]) {
disT[j] = disT[minIndex] + T[minIndex][j];
preL[j] = minIndex;
}
}
}
}
}
void findFastestPath(int start) {
fill(disT, disT + maxn, 0x3fffffff);
fill(num, num + maxn, 0x3fffffff);
disT[start] = 0;
num[start] = 1;
for (int i = 0; i < N; ++i) {
int minD = 0x3fffffff;
int minIndex = -1;
for (int j = 0; j < N; ++j) {
if (!visited[j] && disT[j] < minD) {
minD = disT[j];
minIndex = j;
}
}
// 剩下的节点不连通
if (minIndex == -1) return;
visited[minIndex] = true;
// 根据minIndex更新disT数组
for (int j = 0; j < N; ++j) {
if (!visited[j] && T[minIndex][j] != 0) {
if (disT[j] > disT[minIndex] + T[minIndex][j]) {
disT[j] = disT[minIndex] + T[minIndex][j];
preT[j] = minIndex;
num[j] = num[minIndex] + 1;
} else if (disT[j] == disT[minIndex] + T[minIndex][j] && num[j] > num[minIndex] + 1) {
preT[j] = minIndex;
num[j] = num[minIndex] + 1;
}
}
}
}
}
// 判断最短距离和最短耗时路径是否一样
bool isSame() {
int t = End;
while(preT[t]==preL[t]){
if(preT[t]==Start){
return true;
}
t = preT[t];
}
return false;
}
// 输出最短路径
void printPath(int end, int path[]) {
if (Start == end) {
printf("%d", Start);
return;
}
printPath(path[end], path);
printf(" -> ");
printf("%d", end);
}
int main() {
scanf("%d %d", &N, &M);
int v1, v2, oneWay, length, time;
for (int i = 0; i < M; ++i) {
scanf("%d %d %d %d %d", &v1, &v2, &oneWay, &length, &time);
L[v1][v2] = length;
T[v1][v2] = time;
if (oneWay == 0) {
// 双向边
L[v2][v1] = length;
T[v2][v1] = time;
}
}
scanf("%d %d", &Start, &End);
findShortestPath(Start);
memset(visited, 0, sizeof(visited));
findFastestPath(Start);
if (isSame()) {
printf("Distance = %d; Time = %d: ", disL[End], disT[End]);
printPath(End, preL);
} else {
printf("Distance = %d: ", disL[End]);
printPath(End, preL);
printf("\n");
printf("Time = %d: ", disT[End]);
printPath(End, preT);
}
return 0;
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。