Roadblocks-路障-Dijkstra、Spfa

179 阅读4分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。​

Bessie has moved to a small farm and sometimes enjoys returning to visit one of her best friends. She does not want to get to her old home too quickly, because she likes the scenery along the way. She has decided to take the second-shortest rather than the shortest path. She knows there must be some second-shortest path.

The countryside consists of R (1 ≤ R ≤ 100,000) bidirectional roads, each linking two of the N (1 ≤ N ≤ 5000) intersections, conveniently numbered 1..N. Bessie starts at intersection 1, and her friend (the destination) is at intersection N.

The second-shortest path may share roads with any of the shortest paths, and it may backtrack i.e., use the same road or intersection more than once. The second-shortest path is the shortest path whose length is longer than the shortest path(s) (i.e., if two or more shortest paths exist, the second-shortest path is the one whose length is longer than those but no longer than any other path).

Input

Line 1: Two space-separated integers: N and R 
Lines 2.. R+1: Each line contains three space-separated integers: AB, and D that describe a road that connects intersections A and B and has length D (1 ≤ D ≤ 5000)

Output

Line 1: The length of the second shortest path between node 1 and node N

Sample Input

4 4
1 2 100
2 4 200
2 3 250
3 4 100

Sample Output

450

Hint

Two routes: 1 -> 2 -> 4 (length 100+200=300) and 1 -> 2 -> 3 -> 4 (length 100+250+100=450)

题意:有N个路口,R条道路,道路可以双向通行。问1号路口到N号路口的次短路长度,同一条边可以经过多次。

题解:最短路问题求解的是图中两点间最短路径,次短路指的是比最短路径长的次短路径。用dijksta求解次短路要用两个数组分别存放最短路径和次短路径,在遍历图时,也要分别记录是否访问过。每次找到比当前最短路还要短的路径时,那么当前最短路径的值赋给次短路,找到的更小值赋给最短路径。  找到大于当前最短路径且小于当前次短路径的路径,就将其值赋给当前次短路。

 一:spfa

#include<stdio.h>
#include<string.h>
#include<queue>
#include<algorithm>
using namespace std;
#define inf 0x3f3f3f3f
int v[200200],w[200200],u[200200];
int dis1[200200],dis2[200200];
int book[200200];
int first[200200],next[200200];///用邻接表存图
int n,m,k;
void add(int a,int b,int c)
{
    u[k]=a,v[k]=b,w[k]=c;
    next[k]=first[a];///first[a]保存顶点a的第一条边的编号,
    first[a]=k; ///next[k]存储“编号为k的边”的“下一条边”的编号
    k++;
}
void spfa(int x,int dis[])///数组返回首地址
{
    memset(book,0,sizeof(book));
    queue<int>Q;
    Q.push(x);
    dis[x]=0;
    book[x]=1;
    while(!Q.empty())
    {
        int u=Q.front();
        Q.pop();
    book[u]=0;///找到now号顶点的第一条边之后,剩下的都可以在next数组中依次找到
        for(int j=first[u]; j!=-1; j=next[j])///x号顶点的第一条边
            if(dis[v[j]]>dis[u]+w[j])
            {
                dis[v[j]]=dis[u]+w[j];
                if(book[v[j]]==0)
                {
                    book[v[j]]=1;
                    Q.push(v[j]);
                }
            }
    }
}
int main()
{
    while(~scanf("%d %d",&n,&m))
    {
        k=0;///初始化first数组为-1,表示1~n顶点暂时都没有边
        memset(first,-1,sizeof(first));
        memset(dis1,inf,sizeof(dis1));
        memset(dis2,inf,sizeof(dis2));
        for(int i=0; i<m; i++)
        {
            int a,b,c;
            scanf("%d %d %d",&a,&b,&c);
            add(a,b,c);
            add(b,a,c);
        }
        spfa(1,dis1);///以a为起点进行一次spfa,得到的数组为dis1[]
        spfa(n,dis2);///以b为起点进行spfa,得到数组为dis2[]
        int mi=inf;
        for(int i=1; i<=n; i++)///枚举所给的每一条直连边
        {
            for(int j=first[i]; j!=-1; j=next[j])
            {
                int sum=dis1[i]+dis2[v[j]]+w[j];
                if(sum>dis1[n]&&sum<mi)
                    mi=sum;///取这些中大于最短路的最小的一个就是次短路。
            }
        }
        printf("%d\n",mi);
    }
    return 0;
}

二:dijkstra

#include<stdio.h>
#include<string.h>
#include<queue>
#include<algorithm>
using namespace std;
#define inf 0x3f3f3f3f
int v[200200],w[200200],u[200200];
int dis1[200200],dis2[200200];
int book[200200];
int first[200200],next[200200];      ///用邻接表存图
int k,n,r;
void add(int a,int b,int t)
{
    u[k]=a;
    v[k]=b;
    w[k]=t;
    next[k]=first[a];  ///first[a]保存顶点a的第一条边的编号,
    first[a]=k;         ///next[k]存储“编号为k的边”的“下一条边”的编号
    k++;
}
void dijkstra(int x,int dis[]) ///数组返回首地址
{
    memset(book,0,sizeof(book));
    for(int i=1; i<=n; i++)
        dis[i]=inf;     ///初始化每个点之间的路径为无穷
    dis[x]=0;
    queue<int>Q;         ///声明队列
    int now,p;
    now=x;
    Q.push(now);
    while(!Q.empty())
    {
        now=Q.front();
        Q.pop();
        book[now]=0;
        p=first[now];   ///now号顶点的第一条边
        while(p!=-1)
        {
            if(dis[v[p]]>dis[u[p]]+w[p]) ///找最短路径
            {
                dis[v[p]]=dis[u[p]]+w[p];
                if(book[v[p]]==0)
                {
                    book[v[p]]=1; ///标记
                    Q.push(v[p]);
                }
            }
    p=next[p];///找到now号顶点的第一条边之后,剩下的都可以在next数组中依次找到
        }
    }
}
int main()
{
    while(~scanf("%d %d",&n,&r))
    {
        int a,b,t;
        k=1;
  memset(first,-1,sizeof(first));///初始化first数组为-1,表示1~n顶点暂时都没有边
        for(int i=1; i<=r; i++)
        {
            scanf("%d %d %d",&a,&b,&t);
            add(a,b,t);
            add(b,a,t);
        }
        dijkstra(1,dis1);///1为起点进行一次dijkstra,得到数组dis1[]
        dijkstra(n,dis2);///n为起点进行一次dijkstra,得到数组dis2[]
        int minn=inf;
        for(int i=1; i<=2*r; i++)///枚举所给的每一条直连边
        {
            if(dis1[u[i]]+dis2[v[i]]+w[i]>dis1[n])
                minn=min(minn,dis1[u[i]]+dis2[v[i]]+w[i]);
        }///取这些中大于最短路的最小的一个就是次短路
        printf("%d\n",minn);
    }
    return 0;
}