迷阵突围——Dijkstra求次短路

问题描述

蒜头君陷入了坐标系上的一个迷阵,迷阵上有 n 个点,编号从 1 到 n。蒜头君在编号为 1 的位置,他想到编号为 n 的位置上。蒜头君当然想尽快到达目的地,但是他觉得最短的路径可能有风险,所以他会选择第二短的路径。现在蒜头君知道了 n 个点的坐标,以及哪些点之间是相连的,他想知道第二短的路径长度是多少。
注意,每条路径上不能重复经过同一个点。
输入格式
第一行输入两个整数 n (1≤n≤200) 和 m,表示一共有 n 个点和 m 条边。
接下来输入 n 行,每行输入两个整数xi,yi(−500≤xi,yi≤500),代表第 ii 个点的坐标。
接下来输入 mm 行,每行输入两个整数 pj,qj(1≤pj,qj≤n),表示点pj和点 qj之间相连。
输出格式
输出一行,输出包含一个数,表示第二短的路径长度(小数点后面保留两位),如果第一短路径有多条,则答案就是第一最短路径的长度;如果第二最短路径不存在,则输出 −1。
样例输入
3 3
1 1
2 2
3 2
1 2
2 3
1 3
样例输出
2.41

思路

先求出最短路,然后在依次删除这条最短路上的点及相连的边,每删一求次出此时的最短路即为次短路,取所有次短路中最小值即为此图的次短路,提示:对最短路为起点到终点仅有一条有向边的情况需要特殊处理

#include<bits/stdc++.h>
#include<cstring>
using namespace std;

const int N = 205;

vector<int> v_pos[N];

double g[N][N];

double dis[N];

bool vis[N];

int n, m;

int pre[N];  //用一个数组记录当前点是由哪个点更新的,例如pre[2] = 1 ,即结点2由1更新得到

void Dijkstra(int u,int dele_v){
    memset(dis,0x7f,sizeof dis);
    dis[u] = 0;
    pre[1] = 1;  //结点1由自身得到的

    for(int i = 0 ;i < n; ++i){

        double min_d = dis[0];
        int min_v = -1;

        for(int j = 1; j <= n; ++j){
            if(j != dele_v && !vis[j] && dis[j] < min_d){
                min_d = dis[j];
                min_v = j;
            }
        }

        if(min_v == -1){  //说明单源最短路已经找齐
            return;
        }

        vis[min_v] = true;  //标记

        for(int k = 1; k <= n; ++k){  //遍历和j相邻的点
            if(k != dele_v && min_v != k && !vis[k] && dis[k] > min_d + g[min_v][k]){
                dis[k] = min_d + g[min_v][k];
                pre[k] = min_v;
            }
        }
    }
}


int main(){
    memset(g,0x7f,sizeof g);

    cin >> n >> m;
    int i = 1;
    int  t = n;
    while(t--){
        int x,y;
        cin >> x >> y;
        v_pos[i].push_back(x);
        v_pos[i].push_back(y);
        ++i;
    }
    while(m--){
        int u,v;
        cin >> u >> v;
        double len = sqrt((v_pos[u][0]-v_pos[v][0])*(v_pos[u][0]-v_pos[v][0])+(v_pos[u][1]-v_pos[v][1])*(v_pos[u][1]-v_pos[v][1]));
        g[u][v] = g[v][u] = len;
    }

    Dijkstra(1,0);

    double ans = g[0][0];

    if(pre[n] == 1){     //如果刚好从1到n最短路前一个结点就是1,即最短路就是1和n的边权,需要特殊处理
        g[1][n] = g[n][1] = g[0][0];  //删除从1到n的无向边
        memset(vis,0,sizeof vis);    //注意!!!! 每用一次Dijkstra,就得重置一次vis
        Dijkstra(1,0);
        ans = min(ans,dis[n]);
    }
    else{              //否则,依次删去最短路除头尾结点的各个结点,再进行Dijkstra算法,找出删去结点的最短路的最小值,即次短路
        for(int p = pre[n]; p != 1; p = pre[p]){
            memset(vis,0,sizeof vis);    //注意!!!! 每用一次Dijkstra,就得重置一次vis
            Dijkstra(1,p);    //对最短路上有其他点的情况,只需依次删除点即可
            ans = min(dis[n], ans);

        }
    }

    printf("%.2f\n",ans);

    return 0;
}

 

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值