跳转至

次短路算法

每次更新时,优先更新最短路,若能更新最短路,就令次短路等于之前的最短路,再更新最短路

次短路

有两种比较简单实现次短路的思想

方法一:用 dijkstra 算法 从起点开始 同时维护 最短路数组dis1[ ] 和 次短路 数组 dis2[ ]

方法二:还是用到dijkstra 算法 分别用两个 dis1[ ] 数组 和 dis2[ ] 数组 分别 维护 从起点 和 从终点开始 的 最短路,然后枚举 所有边 , 将边的两个端点 连上 起点和终点 看是不是等于最短路,相等则跳过 , 不相等 则 更新 和 次短路(Inf)取min

例题 #1 集合位置

题目描述

每次有大的活动,大家都要在一起“聚一聚”,不管是去好乐迪,还是避风塘,或者汤姆熊,大家都要玩的痛快。还记得心语和花儿在跳舞机上的激情与释放,还记得草草的投篮技艺是如此的高超,还记得狗狗的枪法永远是'S'……还有不能忘了,胖子的歌声永远是让我们惊叫的!!

今天是野猫的生日,所以想到这些也正常,只是因为是上学日,没法一起去玩了。但回忆一下那时的甜蜜总是一种幸福嘛。。。

但是每次集合的时候都会出现问题!野猫是公认的“路盲”,野猫自己心里也很清楚,每次都提前出门,但还是经常迟到,这点让大家很是无奈。后来,野猫在每次出门前,都会向花儿咨询一下路径,根据已知的路径中,总算能按时到了。

现在提出这样的一个问题:给出 \(n\) 个点的坐标,其中第一个为野猫的出发位置,最后一个为大家的集合位置,并给出哪些位置点是相连的。野猫从出发点到达集合点,总会挑一条最近的路走,如果野猫没找到最近的路,他就会走第二近的路。请帮野猫求一下这条第二最短路径长度。

输入格式

第一行是两个整数 \(n(1 \le n \le 200)\)\(m\),表示一共有 \(n\) 个点和 \(m\) 条路,以下 \(n\) 行每行两个数 \(x_i\)\(y_i\)\((-500 \le x_i,y_i \le 500),\) 代表第 \(i\) 个点的坐标,再往下的 \(m\) 行每行两个整数 \(p_j\)\(q_j,(1 \le p_j,q_j \le n)\),表示两个点相通。


正确算法

我们采用删边的思想,先跑一遍最短路,记录路径。

然后依次删去最短路径上每一条边,分别跑一遍最短路,取所有答案中的最小值即可。

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+5;
const int INF=1e9+5;
int vis[N],h[N],used[N],lst[N];
int cnt,u,w,n,m,v,s;
#define pdi pair<double,int>
using namespace std;
struct node{
    double x,y;
    int head;
    double dis;
    int pre;
}node[205];
struct edge{
    int nxt,to; 
    double dis;
}edge[50005];
double ans=INF<<1;
double calc(double a,double b,double c,double d){
    return (double)sqrt(double(a-c)*double(a-c)+double(b-d)*double(b-d));
}
//距离公式,注意数据类型
void addEdge(int u,int v,double w){
    edge[++cnt].dis=w;
    edge[cnt].to=v;
    edge[cnt].nxt=node[u].head;
    node[u].head=cnt;
}
priority_queue<pdi,vector<pdi>,greater<pdi> >pq;
void djkstr(int x,int y){
    for(int i=1;i<=n;i++){
        node[i].dis=INF;
    }
    node[1].dis=0;
    pq.push({0,1});
    while(pq.size()){
        pdi tmp=pq.top();
        pq.pop();
        double d=tmp.first;
        int u=tmp.second;
        if(node[u].dis!=d)continue;
        for(int e=node[u].head;e;e=edge[e].nxt) {
            int v=edge[e].to;
            if((u==x&&v==y)||(u==y&&v==x))continue;
            if(node[v].dis<=d+edge[e].dis) continue;
            if(x==-1&&y==-1)node[v].pre=u;
            node[v].dis=d+edge[e].dis;
            pq.push({node[v].dis,v});
        }
    }
}
signed main(){
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%lf%lf",&node[i].x,&node[i].y);
    }

    for(int i=1,u,v;i<=m;i++){
        scanf("%lld%lld",&u,&v);
        double w=calc(node[u].x,node[u].y,node[v].x,node[v].y);
        addEdge(u,v,w);
        addEdge(v,u,w);
    }

    djkstr(-1,-1);

    for(int i=n;i!=1;i=node[i].pre) {
        djkstr(i,node[i].pre);
        ans=min(ans,node[n].dis);
    }
    if(ans>=INF)puts("-1");
    else printf("%.2lf\n",ans);
    return 0;
}

以下是未验证的其他算法

写法1

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+5;
const int INF=1e9+5;
int vis[N],h[N],used[N],lst[N];
int cnt,u,w,n,m,v,s;

double ans=INF,dis[3][N];
struct node {
    int nxt;
    double dis;
};

struct point {
    double x,y;
}p[N];

double getdis(int a,int b){
    return sqrt((p[a].x-p[b].x)*(p[a].x-p[b].x)+(p[a].y-p[b].y)*(p[a].y-p[b].y));
}

vector<node> e[N];
priority_queue<pair<double,int>> pq;
void add(int a,int b,double dis) {

//  cerr<<dis<<endl;
    e[a].push_back((node){b,dis});
    e[b].push_back((node){a,dis});
}

void djstr(int rt,int k) {
    pq.push(make_pair(0.00,rt));
    int u=rt;   //先从起点开始查
    for(int i=1; i<=n; i++)dis[k][i]=INF;   //初始化边权

    dis[k][rt]=0.0;
//  vis[rt]=1;//别写错!!

    while(pq.size()) {  //搜完全图

        u=pq.top().second;

        pq.pop();
        if(vis[u])continue;//记得continue
        vis[u]=1;
        for(int i=0;i<e[u].size();i++){
            int v=e[u][i].nxt;
            double w=e[u][i].dis;
            if(!vis[v]&&dis[k][u]+w<dis[k][v]){
                dis[k][v]=dis[k][u]+w;  //更新
//              cerr<<dis[k][v]<<endl;
                pq.push(make_pair(-dis[k][v],v));
                if(k==0)lst[v]=u;
            }
        }
    }
}
signed main() {

    cin>>n>>m;

    for(int i=1;i<=n;i++){
        cin>>p[i].x>>p[i].y;
    }
    for(int i=1;i<=m;i++){
        int a,b;
        cin>>a>>b;
        add(a,b,getdis(a,b));
    }
    djstr(1,0);
//  cerr<<"OK";
    memset(vis,0,sizeof vis);

    djstr(n,1);

//  for(int i=1;i<=n;i++){
//      cerr<<lst[i]<<' ';
//  }
//  
    for(int i=n;i;i=lst[i]){
        used[i]=1;
    }
//  cerr<<"YES";
    for(int i=1;i<=n;i++){
        if(used[i])continue;
//      cerr<<i<<"has ans="<<dis[0][i]+dis[1][i]<<endl;
        ans=min(ans,dis[0][i]+dis[1][i]);
    }
    double mx=INF;
    if(mx==ans)cout<<-1;
    else printf("%.2lf",ans);
    return 0;
}

写法2

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+5;
const int INF=1e9+5;
int vis[N],h[N],used[N],lst[N];
int cnt,u,w,n,m,v,s;

double ans=INF,dis[N],sdis[N];
struct node {
    int nxt;
    double dis;
};

struct point {
    double x,y;
}p[N];

double getdis(int a,int b){
    return sqrt((p[a].x-p[b].x)*(p[a].x-p[b].x)+(p[a].y-p[b].y)*(p[a].y-p[b].y));
}

vector<node> e[N];
priority_queue<pair<double,int>> pq;
void add(int a,int b,double dis) {

//  cerr<<dis<<endl;
    e[a].push_back((node){b,dis});
    e[b].push_back((node){a,dis});
}

void merge(int x,double d){
    // cerr<<"mer p:"<<x<<" d="<<d<<endl;
    if(dis[x]>d){
        sdis[x]=min(sdis[x],dis[x]);
        dis[x]=d;
    }else if(sdis[x]>d)sdis[x]=d;
}

void djstr(int rt) {
    pq.push(make_pair(0.00,rt));
    int u=rt;   //先从起点开始查
    for(int i=1; i<=n; i++)dis[i]=INF;  //初始化边权
    for(int i=1; i<=n; i++)sdis[i]=INF; //初始化边权

    dis[rt]=0.0;
//  vis[rt]=1;//别写错!!

    while(pq.size()) {  //搜完全图

        u=pq.top().second;

        pq.pop();
        if(vis[u])continue;//记得continue
        vis[u]=1;
        for(int i=0;i<e[u].size();i++){
            int v=e[u][i].nxt;
            double w=e[u][i].dis;
            if(!vis[v]&&dis[u]+w<sdis[v]){
                // dis[v]=dis[u]+w; //更新
                merge(v,dis[u]+w);
//              cerr<<dis[v]<<endl;
                pq.push(make_pair(-dis[v],v));
            }
        }
    }
}
signed main() {

    cin>>n>>m;

    for(int i=1;i<=n;i++){
        cin>>p[i].x>>p[i].y;
    }
    for(int i=1;i<=m;i++){
        int a,b;
        cin>>a>>b;
        add(a,b,getdis(a,b));
    }
    djstr(1);
    double mx=INF;
    ans=sdis[n];
    // cerr<<dis[n]<<' '<<sdis[n]<<endl;
    if(mx==ans)cout<<-1;
    else printf("%.2lf",ans);
    return 0;
}