题目
题目大意
给出城市个数n,边的数目m,接下来的m行以v1 v2 one-way length time的形式给出各节点的连通情况。如果one-way = 1,那么这条边是单向的,如果 = 0,则这条边是双向的。最后给出起点和终点。要求输出最短路径,如果有多条最短路径,选择时间花费最少的那条;要求输出时间最少路径,如果有多条时间最少路径,选择通过节点个数最少的路径。如果最短路径和时间最少路径完全一致,则输出一行;若不一致,输出两行。不仅要输出最短距离或最少时间,也要输出路径的每个节点。
思路
用两次迪杰斯特拉算法,一次计算最短路径,另一次计算时间最少的路径。然后输出路径用递归函数来回溯解决。需要注意的是,计算时间最少路径中,通过节点数最少的才是最佳路径,因此需要加一个nodenum数组来计算每个路径的节点数,如果不考虑这种情况,测试点4是过不了的。剩下的根据题意模拟就可以了。
代码
#include <iostream>
#include <vector>
#include <climits>
using namespace std;
int n, m, start, des;
int g[510][510] = {0}; // 图
int Time[510][510] = {0}; // 时间矩阵,用time会与C++的time()起冲突
vector<int> dist; // 最短路径的长度
vector<int> mintime; // 最少时间的路径
vector<int> path; // 记录每个最短路径节点的前驱节点
vector<int> tpath; // 记录每个最少时间路径节点的前驱节点
vector<int> res; // 记录最佳最短路径的节点
vector<int> tres; // 记录最佳最少时间路径的节点
vector<int> nodenum; // 记录路径经过的节点数量
void dijie(){
bool visited[510] = {false};
dist[start] = 0;
mintime[start] = 0;
for (int cnt = 0; cnt < n; cnt++){
int now = -1, minl = INT_MAX;
for (int i = 0; i < n; i++){
if (!visited[i] && dist[i] < INT_MAX){
if (minl > dist[i]){
minl = dist[i];
now = i;
}
}
} // 找到要激活的节点
if (now == -1){
return;
}
visited[now] = true; // 找到要激活的节点
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];
mintime[i] = mintime[now] + Time[now][i];
path[i] = now; // 记录前驱节点
}else if (dist[i] == dist[now] + g[now][i]){
if (mintime[i] > mintime[now] + Time[now][i]){
mintime[i] = mintime[now] + Time[now][i];
path[i] = now; // 记录前驱节点
}
}
}
}
}
}
void dijieTime(){
bool visited[510] = {false};
mintime[start] = 0;
for (int cnt = 0; cnt < n; cnt++){
int now = -1, mint = INT_MAX;
for (int i = 0; i < n; i++){
if (!visited[i] && mintime[i] < INT_MAX){
if (mint > mintime[i]){
mint = mintime[i];
now = i;
}
}
}
if (now == -1){
return;
}
visited[now] = true;
for (int i = 0; i < n; i++){
if (!visited[i] && Time[now][i] < INT_MAX){
if (mintime[i] > mintime[now] + Time[now][i]){
mintime[i] = mintime[now] + Time[now][i];
tpath[i] = now; // 记录前驱节点
nodenum[i] = nodenum[now] + 1; // 更新节点个数
}else if (mintime[i] == mintime[now] + Time[now][i]){
if (nodenum[i] > nodenum[now] + 1){
nodenum[i] = nodenum[now] + 1;
tpath[i] = now;
} // 测试点4
}
}
}
}
}
void printPath(int node){
if (node == start){
res.push_back(start);
return;
}
printPath(path[node]);
res.push_back(node);
} // 记录path
void printTpath(int node){
if (node == start){
tres.push_back(start);
return;
}
printTpath(tpath[node]);
tres.push_back(node);
} // 记录tpath
int main(){
cin >> n >> m;
for (int i = 0; i < n; i++){
for (int j = 0; j < n; j++){
g[i][j] = (i == j) ? 0 : INT_MAX;
Time[i][j] = (i == j) ? 0 : INT_MAX;
}
} // 初始化图和时间矩阵
for (int i = 0; i < m; i++){
int v1, v2, way, length, t;
cin >> v1 >> v2 >> way >> length >> t;
if (way == 1){
g[v1][v2] = length;
Time[v1][v2] = t;
}else{
g[v1][v2] = g[v2][v1] = length;
Time[v1][v2] = Time[v2][v1] = t;
}
} // 构造图和时间矩阵
cin >> start >> des;
dist.resize(n, INT_MAX);
path.resize(n, -1);
tpath.resize(n, -1);
mintime.resize(n, INT_MAX);
nodenum.resize(n, 0);
dijie();
printPath(des);
fill(mintime.begin(), mintime.end(), INT_MAX);
dijieTime();
printTpath(des);
int flag = 1; // 输出一行
if ((int)res.size() != (int)tres.size()){
flag = 0; // 输出两行
}else{
for (int i = 0; i < (int)res.size(); i++){
if (res[i] != tres[i]){
flag = 0;
break;
}
}
}
if (flag){
cout << "Distance = " << dist[des] << "; Time = " << mintime[des] << ": ";
for (int i = 0; i < (int)tres.size(); i++){
cout << tres[i];
if (i != (int)tres.size() - 1){
cout << " -> ";
}
}
cout << endl;
}else{
cout << "Distance = " << dist[des] << ": ";
for (int i = 0; i < (int)res.size(); i++){
cout << res[i];
if (i != (int)res.size() - 1){
cout << " -> ";
}
}
cout << endl;
cout << "Time = " << mintime[des] << ": ";
for (int i = 0; i < (int)tres.size(); i++){
cout << tres[i];
if (i != (int)tres.size() - 1){
cout << " -> ";
}
}
cout << endl;
}
return 0;
}