1111 Online Map (30 分)

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;
    }
}

注意点

这道题目整体的思路并不难,并且可以套用现成的模板,但是很多细节很容易出错,比如:

  1. 找出最短路径后因为是逆序存放结点的 并且 是有向图,所以遍历时得倒着来,否则会出错
  2. 在Dijkstra时,d[s2]或c[s2]就是我们第一标尺的最优值。
  3. 在dfs时,在递归边界处才会形成一条临时路径,第二标尺的最优值也需要在这里求出。
  4. vecotr pre1[MAXV];存放的是以距离为第一标尺的所有最短路径(pre1[v]中存储的是最短路径下顶点v的所有前驱顶点)
  5. vector path;存放的是一条第二标尺下最优的最短路径(path中逆序存放着这一条路径上的所有顶点)
  6. 这里的MIN和optValue需要初始化为INF才逻辑合理
  7. 第一次Dj+dfs后数组visit需要重新初始化
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值