1111 Online Map (30 分)
题目大意
输入我们的当前位置和目的地,在线地图可以推荐多条路径。现在,您的工作是向用户推荐两条路径:一条是最短的,另一条是最快的。可以保证任何请求都存在路径。
核心思路
可以套用晴神算法笔记上总结的模板,进行两个Dijkstra+DFS即可:
- 第一次Dijkstra以距离为第一标尺求出所有最短路径,DFS以时间为第二标尺在找出的所有距离最短路径中找一条时间最短的。
- 第二次Dijkstra以时间为第一标尺求出所有最短路径,DFS以距离为第二标尺在找出的所有时间最短路径中找一条距离最短的。
代码
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int MAXV=510;
const int INF=1000000000;
int n,m;//顶点数,边数
int s1,s2;//起点,终点
int visit[MAXV];//表示顶点是否被访问,即是否被放入到集合S中
int G[MAXV][MAXV];//存放两个顶点间的距离。初始化所有顶点之间的距离为INF,然后从题目中读入所有边的距离
int d[MAXV];//从起点开始到达各个顶点的最短路径。初始化起点到起点的距离d[s]为0
int cost_time[MAXV][MAXV];//存放两个顶点间的时间消耗。初始化所有顶点之间的时间消耗为INF,然后从题目中读入所有边的时间花费
int c[MAXV];//从起点开始到达各个顶点的最小时间。初始化起点到起点的最短距离c[s]为0
vector<int> pre1[MAXV];//以距离为第一标尺的所有最短路径(pre1[v]中存储的是最短路径下顶点v的所有前驱顶点)
vector<int> pre2[MAXV];//以时间为第一标尺的所有最短路径(pre2[v]中存储的是最短路径下顶点v的所有前驱顶点)
void Dijkstra(int s){
//初始化数组d[]
fill(d,d+MAXV,INF);
d[s]=0;
//循环n次,把所有顶点放入集合S
for(int i=0;i<n;i++){
//寻找当前未访问结点中离起点s最近的顶点u
int u=-1,MIN=INF;
for(int j=0;j<n;j++){
if(visit[j]==false&&d[j]<MIN){
u=j;
MIN=d[j];
}
}
if(u==-1) return;//已经没有顶点可以到达起点s,直接返回
else visit[u]=true;//置该顶点为为已访问,即把顶点u放入集合S
//开放与该顶点相连的所有边,查看通过点u是否能让未访问顶点的相连顶点v的d[v]减小。
//如果能,则更新d[v]并记录该前驱顶点
for(int v=0;v<n;v++){
if(visit[v]==false&&G[u][v]!=INF){
if(d[u]+G[u][v]<d[v]){
d[v]=d[u]+G[u][v];
pre1[v].clear();
pre1[v].push_back(u);
}else if(d[u]+G[u][v]==d[v]){
pre1[v].push_back(u);
}
}
}
}
}
void Dijkstra1(int s){
//初始化数组d[]
fill(c,c+MAXV,INF);
c[s]=0;
//循环n次,把所有顶点放入集合S
for(int i=0;i<n;i++){
//寻找当前未访问结点中离起点s最近的顶点u
int u=-1,MIN=INF;
for(int j=0;j<n;j++){
if(visit[j]==false&&c[j]<MIN){
u=j;
MIN=d[j];
}
}
if(u==-1) return;//已经没有顶点可以到达起点s,直接返回
else visit[u]=true;//置该顶点为为已访问,即把顶点u放入集合S
//开放与该顶点相连的所有边,查看通过点u是否能让未访问顶点的相连顶点v的d[v]减小。
//如果能,则更新d[v]并记录该前驱顶点
for(int v=0;v<n;v++){
if(visit[v]==false&&cost_time[u][v]!=INF){
if(c[u]+cost_time[u][v]<c[v]){
c[v]=c[u]+cost_time[u][v];
pre2[v].clear();
pre2[v].push_back(u);
}else if(c[u]+cost_time[u][v]==c[v]){
pre2[v].push_back(u);
}
}
}
}
}
int optValue=INF;//最优路径下第二标尺的值
vector<int> optPath, tempPath;//最优路径、临时路径
void dfs(int v){
if(v==s1){
tempPath.push_back(v);
//到这里已经求出了一整条临时路径,求这条路径下的临时第二标尺tempValue,如果该值优于optValue,则更新最优路径和optValue
int tempValue = 0; //临时路径下路径下第二标尺的值
for (int i = tempPath.size()-1; i >0;i--){
int pos = tempPath[i], posNext = tempPath[i - 1];
tempValue += cost_time[pos][posNext];
}
if(tempValue<optValue){
optValue = tempValue;
optPath = tempPath;
}
tempPath.pop_back();
return;
}
tempPath.push_back(v);
for (int i = 0; i < pre1[v].size();i++){
dfs(pre1[v][i]);
}
tempPath.pop_back();
}
int optValue1=INF;//最优路径下第二标尺的值
vector<int> optPath1, tempPath1;//最优路径、临时路径
void dfs1(int v){
if(v==s1){
tempPath1.push_back(v);
//到这里已经求出了一整条临时路径,求这条路径下的临时第二标尺tempValue,如果该值优于optValue,则更新最优路径和optValue
int tempValue1 = 0; //临时路径下路径下第二标尺的值
for (int i = tempPath1.size()-1; i >0;i--){
int pos = tempPath1[i], posNext = tempPath1[i - 1];
tempValue1 += G[pos][posNext];
}
if(tempValue1<optValue1){
optValue1 = tempValue1;
optPath1 = tempPath1;
}
tempPath1.pop_back();
return;
}
tempPath1.push_back(v);
for (int i = 0; i < pre2[v].size();i++){
dfs1(pre2[v][i]);
}
tempPath1.pop_back();
}
int main(){
//读入并初始化数据
cin>>n>>m;
fill(G[0],G[0]+MAXV*MAXV,INF);
fill(cost_time[0],cost_time[0]+MAXV*MAXV,INF);
for(int i=0;i<m;i++){
int v1,v2,a,b,c;
cin>>v1>>v2>>a>>b>>c;
if(a==1){
G[v1][v2]=b;
cost_time[v1][v2]=c;
}else{
G[v1][v2]=G[v2][v1]=b;
cost_time[v1][v2]=cost_time[v2][v1]=c;
}
}
cin>>s1>>s2;
//以距离为第一标尺,时间为第二标尺进行Dijkstra+DFS算法
Dijkstra(s1);//基于第一标尺,找到所有最优的路径
dfs(s2); //在找出的所有路径中寻找一条第二标尺最优的路径
//以时间为第一标尺,距离为第二标尺进行Dijkstra+DFS算法
fill(visit, visit + MAXV, false);//重新初始化,供下一次Dijkstra+DFS利用
Dijkstra1(s1);//基于第一标尺,找到所有最优的路径
dfs1(s2);在找出的所有路径中寻找一条第二标尺最优的路径
if(optPath==optPath1){
cout << "Distance = " << d[s2] << "; Time = " << c[s2] << ": " << optPath[optPath.size() - 1];
for (int i = optPath.size() - 2; i >= 0;i--){
cout << " -> " << optPath[i];
}
}else{
cout << "Distance = " << d[s2] << ": " << optPath[optPath.size() - 1];
for (int i = optPath.size() - 2; i >= 0;i--){
cout << " -> " << optPath[i];
}
cout << endl;
cout << "Time = " << c[s2] << ": " << optPath1[optPath1.size() - 1];
for (int i = optPath1.size() - 2; i >= 0;i--){
cout << " -> " << optPath1[i];
}
cout << endl;
}
}
注意点
这道题目整体的思路并不难,并且可以套用现成的模板,但是很多细节很容易出错,比如:
- 找出最短路径后因为是逆序存放结点的 并且 是有向图,所以遍历时得倒着来,否则会出错
- 在Dijkstra时,d[s2]或c[s2]就是我们第一标尺的最优值。
- 在dfs时,在递归边界处才会形成一条临时路径,第二标尺的最优值也需要在这里求出。
- vecotr pre1[MAXV];存放的是以距离为第一标尺的所有最短路径(pre1[v]中存储的是最短路径下顶点v的所有前驱顶点)
- vector path;存放的是一条第二标尺下最优的最短路径(path中逆序存放着这一条路径上的所有顶点)
- 这里的MIN和optValue需要初始化为INF才逻辑合理
- 第一次Dj+dfs后数组visit需要重新初始化