• 最短路的几种算法和模板


    最短路分为单源最短路和多源汇最短路;

    1单源最短路根据边长的正负分为两类(n表示点,m表示边)

      (1)边长为正

        dijkstra算法

        朴素版(o(n^2))

        堆优化版(0(mlogn))

       当稠密图(m>=n^2)时朴素版的时间更优,稀疏图则用堆优化版更优;

      (2)边权有负

         bellman_ford算法  0(nm)

         由于SPFA算法是bellman_ford的优化,bellman_ford常用于求有边数限制的最短路,如在不经过超过k条边时的最短路,注意这时的不存在最短路的临界条件是dist[n]>0x3f3f3f3f,因为可能存在,dist[t]=0x3f3f3f3f,但存在t到n的边可能松弛dist[n];

        SPFA O(m),最坏O(nm)

         还可用来求负环,也可以用来求正权最短路

    2.多源汇最短路

    folyd算法 0(n^3) 适合稠密图

    dijkstra算法朴素版

    #include<iostream>
    #include<cstring>
    using namespace std;
    int g[510][510];
     int n,m;
    int vis[510],dist[510];
    int dijkstra()
    {
        memset(dist,0x3f,sizeof dist);
        dist[1]=0;
        for(int i=0;i<n;i++)
        {
            int t=-1;
            for(int j=1;j<=n;j++)
            {
                if(!vis[j]&&(t==-1||dist[t]>dist[j]))
                t=j;
            }
            vis[t]=1;
            for(int j=1;j<=n;j++)
            dist[j]=min(dist[j],dist[t]+g[t][j]);
        }
        if(dist[n]==0x3f3f3f3f)return -1;
        return dist[n];
    }
    int main()
    {
        memset(g,0x3f,sizeof g);
       cin>>n>>m;
       while(m--)
       {
           int a,b,c;
           cin>>a>>b>>c;
           g[a][b]=min(g[a][b],c);
           //g[b][a]=min(g[b][a],c);
       }
       cout<<dijkstra()<<endl;
        return 0;
    }

    堆优化版

    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    
    using namespace std;
    typedef pair<int,int> PII;
    
    const int N=100010;
    int h[N],e[N],ne[N],idx,w[N];
    int dist[N];
    bool st[N];
    int n,m;
    
    void add(int a,int b,int c){
        e[idx]=b;
        w[idx]=c;
        ne[idx]=h[a];
        h[a]=idx++;
    }
    int dijktra(){
        memset(dist,0x3f,sizeof dist);
        dist[1]=0;
        priority_queue<PII,vector<PII>,greater<PII>>heap;
        heap.push({0,1});
        while(heap.size()){
            auto t=heap.top();
            heap.pop();
            int ver=t.second,distance=t.first;
            if(distance>dist[ver])continue;
            for(int i=h[ver];i!=-1;i=ne[i]){
                int j=e[i];
                if(dist[j]>distance+w[i])
                 {
                     dist[j]=distance+w[i];
                     heap.push({dist[j],j});
                 }
            }
        }
        if(dist[n]==0x3f3f3f3f)return -1;
        return dist[n];
    
    }
    
    int main(){
        memset(h,-1,sizeof h);
        cin>>n>>m;
        while(m--){
            int a,b,c;
            cin>>a>>b>>c;
            add(a,b,c);
        }
        cout<<dijktra()<<endl;
        return 0;
    }

    bellman_ford求有边数限制

    #include<iostream>
    #include<queue>
    #include<cstring>
    using namespace std;
    int n,m,k;
    int dist[510],bakcup[510];
    struct Edge
    {
        int a,b,w;
    }edge[10010];
    int bellmen_ford()
    {
        memset(dist,0x3f,sizeof dist);
        dist[1]=0;
        for(int i=0;i<k;i++)
        {
            memcpy(bakcup,dist,sizeof dist);//拷贝dist数组到bakcup防止发生串联
            for(int j=0;j<m;j++)
            {
                int a=edge[j].a,b=edge[j].b,w=edge[j].w;
                dist[b]=min(dist[b],bakcup[a]+w);//对每一条边a->b进行松弛
            }
        }
        if(dist[n]>0x3f3f3f3f/2)return -1;
        return dist[n];
    }
    int main()
    {
        cin>>n>>m>>k;
        for(int i=0;i<m;i++)
        {
        int a,b,w;
        cin>>a>>b>>w;
        edge[i]={a,b,w};
        }
        int t=bellmen_ford();
        if(t==-1)printf("impossible
    ");
        else
        printf("%d
    ",dist[n]);
        return 0;
    }

    SPEA求最短路(前提是不存在负环)

    #include<iostream>
    #include<cstring>
    #include<queue>
    using namespace std;
    int h[100010],ne[100010],e[100010],w[100010],idx;
    int dist[100010],st[100010];int n,m;
    void add(int a,int b,int c)
    {
        e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
    }
    int spea()
    {
        memset(dist,0x3f,sizeof dist);
        dist[1]=0;
        queue<int>q;
        st[1]=1;q.push(1);
        while(q.size())
        {
            int t=q.front();
            q.pop();
            st[t]=0;//t出队
            for(int i=h[t];i!=-1;i=ne[i])
            {
                int j=e[i];
                if(dist[j]>dist[t]+w[i])
                {
                    dist[j]=dist[t]+w[i];
                    if(!st[j])
                    {
                        q.push(j);
                        st[j]=1;
                    }
                }
            }
        }
        if(dist[n]==0x3f3f3f3f)return -1;//完全做完后一定为最小值
        return dist[n];
    }
    int main()
    {
        cin>>n>>m;
        memset(h,-1,sizeof h);
        while(m--)
        {
            int a,b,w;cin>>a>>b>>w;
            add(a,b,w);
        }
        int t=spea();
        if(t==-1)printf("impossible
    ");
        else
        printf("%d
    ",t);
        return 0;
    }

    SPFA判断负环

    #include<iostream>
    #include<cstring>
    #include<queue>
    using namespace std;
    int h[100010],ne[100010],e[100010],w[100010],idx;
    int dist[100010],st[100010];int n,m,cnt[100010];
    void add(int a,int b,int c)
    {
        e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
    }
    int spea()
    {
        memset(dist,0x3f,sizeof dist);
        dist[1]=0;
        queue<int>q;
        for(int i=1;i<=n;i++)
        st[i]=1,q.push(i);//负环不仅在1开始的路径上
        while(q.size())
        {
            int t=q.front();
            q.pop();
            st[t]=0;//t出队
            for(int i=h[t];i!=-1;i=ne[i])
            {
                int j=e[i];
                if(dist[j]>dist[t]+w[i])
                {
                    dist[j]=dist[t]+w[i];
                    cnt[j]=cnt[t]+1;//该路径多了一条边
                    if(cnt[j]>=n)return 1;//抽屉原理,经过n条边,即经过n+1个点,必有两个相同的点
                    if(!st[j])
                    {
                        q.push(j);
                        st[j]=1;
                    }
                }
            }
        }
        return 0;
    }
    int main()
    {
        cin>>n>>m;
        memset(h,-1,sizeof h);
        while(m--)
        {
            int a,b,w;cin>>a>>b>>w;
            add(a,b,w);
        }
        int t=spea();
        if(t)printf("Yes
    ");
        else
        printf("No");
        return 0;
    }

    folyd

    #include<iostream>
    #include<cstring>
    using namespace std;
    int g[510][510];
    int main()
    {
        memset(g,0x3f,sizeof g);
        int n,m,k;cin>>n>>m>>k;
        for(int i=1;i<=n;i++)g[i][i]=0;
        while(m--)
        {
            int a,b,c;
            cin>>a>>b>>c;
            g[a][b]=min(c,g[a][b]);
        }
        for(int k=1;k<=n;k++)
         for(int i=1;i<=n;i++)
          for(int j=1;j<=n;j++)
          g[i][j]=min(g[i][j],g[i][k]+g[k][j]);
          while(k--)
          {
              int a,b;
              cin>>a>>b;
              if(g[a][b]>0x3f3f3f3f/2)printf("impossible
    ");
              else
              printf("%d
    ",g[a][b]);
          }
        return 0;
    }
  • 相关阅读:
    【创建型】Singleton模式
    【创建型】Abstract Factory模式 & Factory Method模式
    红黑树插入操作
    红黑树删除操作
    bat命令中的变量声明及使用
    bat(传参情况下)取得当前bat所在的目录路径
    代理模式 -- 大话设计模式
    装饰模式 -- 大话设计模式
    依赖倒转原则 -- 大话设计模式
    开放封闭原则 -- 大话设计模式
  • 原文地址:https://www.cnblogs.com/flyljz/p/11766146.html
Copyright © 2020-2023  润新知