最短路算法

最短路算法

  1. Dijkstra算法(o(nlogn))(权值非负)

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    const int maxn=2e5+7;
    struct Edge
    {
        int to,next;
        ll len;
    }edge[maxn];
    struct Node
    {
        int to;
        ll len;
        bool operator<(const Node& x)const{
            return len>x.len;
        }
        Node(int x,ll y) { to=x; len=y; }
    };
    ll dis[maxn];
    int head[maxn];
    int cnt;
    bool vis[maxn];
    void add_edge(int u,int v,int w)
    {
        edge[++cnt].to=v;
        edge[cnt].len=w;
        edge[cnt].next=head[u];
        head[u]=cnt;
    }
    int main()
    {
       int n,m,s;
       while(scanf("%d%d%d",&n,&m,&s)!=EOF)
       {
           int i,t,u,v;  ll w;
           cnt=0;
           memset(head,-1,sizeof(head));
           for(i=1;i<=m;i++)
           {
               scanf("%d%d%lld",&u,&v,&w);
               add_edge(u,v,w);
           }
           memset(vis,0,sizeof(vis));
           priority_queue<Node>s;
           for(i=1;i<=n;i++) dis[i]=1e9+7;
           dis[1]=0;
           s.push(Node(1,0));
           while(!s.empty())
           {
               //cout<<"xxxxx"<<endl;
               Node o=s.top(); s.pop();
               int u=o.to;
               if(vis[u]) continue;
               vis[u]=1;
               for(i=head[u];i!=-1;i=edge[i].next)
               {
                   ll len=edge[i].len;
                   int v=edge[i].to;
                   //cout<<u<<' '<<v<<' '<<dis[u]<<' '<<dis[v]<<' '<<len<<endl;
                   if(dis[v]>dis[u]+len)
                   {
                       dis[v]=dis[u]+len;
                       s.push(Node(v,dis[v]));
                   }
               }
           }
           for(i=1;i<n;i++) cout<<dis[i]<<' ';
           cout<<dis[n]<<endl;
       }
    }
    
  2. bellman-ford算法(o(n*m))(权值可负)

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    const int maxn=2010;
    const int Inf=1e9+7;
    struct Node
    {
        int to,len;
        Node(int a,int b){
            to=a; len=b;
        }
    };
    vector<Node>s[maxn];
    int dis[maxn];
    int cnt[maxn];
    int n,m;
    int main()
    {
        int T;
        cin>>T;
        while(T--)
        {
            for(int i=1;i<=n;i++) s[i].clear();
            cin>>n>>m;
            int i,t,u,v,w;
            memset(cnt,0,sizeof(cnt));
            for(i=1;i<=m;i++)
            {
                scanf("%d%d%d",&u,&v,&w);
                if(w>=0) { s[u].push_back({v,w}); s[v].push_back({u,w}); }
                else s[u].push_back({v,w});
            }
            for(i=1;i<=n;i++) dis[i]=Inf;
            queue<int>x;
            x.push(1); dis[1]=0;
            int z=0;
            while(!x.empty())
            {
                int u=x.front();
                x.pop();
                if(cnt[u]>=n) { z++; break; }
                for(i=0;i<s[u].size();i++)
                {
                    Node y=s[u][i];
                    int v=y.to,w=y.len;
                    if(w+dis[u]<dis[v])
                    {
                        dis[v]=w+dis[u];
                        cnt[v]=cnt[u]+1;
                        x.push(v);
                    }
                }
            }
            if(z>0) cout<<"YES"<<endl;
            else cout<<"NO"<<endl;
        }
    }
    
  3. bellman算法(o(n*m))

    //注意不能有负边
    
    
  4. flod算法

    for(k=1;k<=n;k++)
    for(i=1;i<=n;i++)
    for(t=1;t<=n;t++)
    if(len[i][t]>len[i][k]+len[k][t])
        len[i][t]=len[i][k]+len[k][t];
    
  5. johnson算法(多源最短路)

    解决单源最短路径问题(Single Source Shortest Paths Problem)的算法包括:

    对于全源最短路径问题(All-Pairs Shortest Paths Problem),可以认为是单源最短路径问题的推广,即分别以每个顶点作为源顶点并求其至其它顶点的最短距离。例如,对每个顶点应用 Bellman-Ford 算法,则可得到所有顶点间的最短路径的运行时间为 O(V2E),由于图中顶点都是连通的,而边的数量可能会比顶点更多,这个时间没有比 Floyd-Warshall 全源最短路径算法 O(V3) 更优。那么,再试下对每个顶点应用 Dijkstra 算法,则可得到所有顶点间的最短路径的运行时间为 O(VE + V2logV),看起来优于 Floyd-Warshall 算法的 O(V3),所以看起来使用基于 Dijkstra 算法的改进方案好像更好,但问题是 Dijkstra 算法要求图中所有边的权值非负,不适合通用的情况。

    在 1977 年,Donald B. Johnson 提出了对所有边的权值进行 “re-weight” 的算法,使得边的权值非负,进而可以使用 Dijkstra 算法进行最短路径的计算。

    我们先自己思考下如何进行 “re-weight” 操作,比如,简单地对每条边的权值加上一个较大的正数,使其非负,是否可行?

       1     1     1
    s-----a-----b-----c
     \              /
       \          /
         \______/
             4
    

    比如上面的图中,共 4 条边,权值分别为 1,1,1,4。当前 s --> c 的最短路径是 {s-a, a-b, b-c} 即 1+1+1=3。而如果将所有边的权值加 1,则最短路径就会变成 {s-c} 的 5,因为 2+2+2=6,实际上导致了最短路径的变化,显然是错误的。

    那么,Johnson 算法是如何对边的权值进行 “re-weight” 的呢?以下面的图 G 为例,有 4 个顶点和 5 条边。

    img

    首先,新增一个源顶点 4,并使其与所有顶点连通,新边赋权值为 0,如下图所示。

    img

    使用 Bellman-Ford 算法 计算新的顶点到所有其它顶点的最短路径,则从 4 至 {0, 1, 2, 3} 的最短路径分别是 {0, -5, -1, 0}。即有 h[] = {0, -5, -1, 0}。当得到这个 h[] 信息后,将新增的顶点 4 移除,然后使用如下公式对所有边的权值进行 “re-weight”:

    w(u, v) = w(u, v) + (h[u] - h[v]).
    

    则可得到下图中的结果:

    img

    此时,所有边的权值已经被 “re-weight” 为非负。此时,就可以利用 Dijkstra 算法对每个顶点分别进行最短路径的计算了。

    Johnson 算法描述如下:

    1. 给定图 G = (V, E),增加一个新的顶点 s,使 s 指向图 G 中的所有顶点都建立连接,设新的图为 G’;
    2. 对图 G’ 中顶点 s 使用 Bellman-Ford 算法计算单源最短路径,得到结果 h[] = {h[0], h[1], … h[V-1]};
    3. 对原图 G 中的所有边进行 “re-weight”,即对于每个边 (u, v),其新的权值为 w(u, v) + (h[u] - h[v]);
    4. 移除新增的顶点 s,对每个顶点运行 Dijkstra 算法求得最短路径;

… h[V-1]};
3. 对原图 G 中的所有边进行 “re-weight”,即对于每个边 (u, v),其新的权值为 w(u, v) + (h[u] - h[v]);
4. 移除新增的顶点 s,对每个顶点运行 Dijkstra 算法求得最短路径;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夏的雨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值