• 最短路的几种算法及其优化(模板)


    一.Dijkstra 算法

        dijkstra算法适用于边权为正的情况,求单源最短路,适用于有向图和无向图

        模板伪代码:

                      清除所有点的标号

                      设d[0]=0,其余d[i]=INF;

                      循环n次{

                                 在所有未标记的节点中,寻找d[i]最小的点x

                                 给x做标记

                                对于从x出发的所有边(x,y)更新d[y]=min(d[y],d[x]+w[x,y]);

                                 }

                      

    memset(v,0,sizeof(v));
    for(int i=0;i<n;++i)
    d[i]=(i==0?0:INF);
    for(int i=0;i<n;++i)
    {
        int x,m=INF;
        for(int j=0;j<n;++j)
        if(!visit[j]&&d[j]<m)
        {
            m=d[j];
            x=j;
        }
        visit[x]=1;
        for(int j=0;j<n;++j)
        d[j]=min(d[j],d[x]+w[x][j]);
     } 

         简单说一下dijkstra的优化:

                  1.储存结构上:邻接矩阵是很占空间的(众所周知),所以我们一般采用邻接表或者边表

                  2.堆优化:因为在dijkstra中有循环n次寻找最小dict的过程,我们可以维护一个小根堆来实现,也就把时间复杂度从n^2降到了n*(logn+E)。

         优化后的dijkstra,自己写的:

    /*
    建图用的邻接表,复杂度O(E*logE)
    */
    
    struct pnode {
        int num;
        int len;
    
        pnode() {}
        pnode(int a, int b) : num(a), len(b) {}//初始化结构体用的,把a复制给num,把b复制给len; 
        bool operator < (const pnode tmp) const {
            return len > tmp.len;
        }
    };
    
    int dis[N];
    bool vis[N];
    int n;
    
    void dijkstra(int s) {
        priority_queue<pnode> q;
        q.push(pnode(s, 0));
        pnode u;
        int v, i, res = inf;
        for(i = 0; i <= n; ++i) dis[i] = inf, vis[i] = false;
        dis[s] = 0;
    
        while(!q.empty()) {
            u = q.top(); q.pop();
            if(u.len != dis[u.num]) continue;/*这是应对优先队列中的重复入队的点,只要最新的那个点就可以了*/
            if(vis[u.num])  continue;
            vis[u.num] = true;
    
            for(i = head[u.num]; i != -1; i = g[i].next) {
                v = g[i].to;
                if(dis[v] > u.len + g[i].val) {
                    dis[v] = u.len + g[i].val;
                    q.push(pnode(v, dis[v]));
                }
            }
        }
    }

    二.Bellman-Ford的优化(也就是SPFA,直接看三吧)

    三.SPFA模板及SPFA的优化

       1.普通SPFA模板(队列化的Bellman-Ford算法):

    int visit[N],dis[N];
    bool SPFA(int s)
    {
        queue<int>q;
        memset(dis,127,sizeof(dis));
        memset(visit,false,sizeof(visit));
    memset(cnt,0s,sizeof(cnt)); dis[s]
    =0; visit[s]=true; q.push(s); while(!q.empty()) { int k=q.front(); q.pop(); visit[k]=false; for(int i=head[k];i;i=edge[i].last)/*边表*/ { if(dis[k]+edge[i].w<dis[edge[i].v]) { dis[edge[i].v]=dis[k]+edge[i].w; if(!visit[edge[i].v]) { q.push(edge[i].v); visit[edge[i].v]=true;
    if(++cnt[edge[i].v]>n) /*如果某一个点的入队次数超过了n次,说明存在负环,返回false*/
    return false; } } } } return true;/*安全跑完了,不存在环*/ }

        2.SPFA的优化

          SPFA算法有两个优化策略SLF和LLL——SLF:Small Label First 策略,设要加入的节点是j,队首元素为i,若dist(j)<dist(i),则将j插入队首,否则插入队尾; LLL:Large Label Last 策略,设队首元素为i,队列中所有dist值的平均值为x,若dist(i)>x则将i插入到队尾,查找下一元素,直到找到某一i使得dist(i)<=x,则将i出队进行松弛操作。 SLF 可使速度提高 15 ~ 20%;SLF + LLL 可提高约 50%。 在实际的应用中SPFA的算法时间效率不是很稳定,为了避免最坏情况的出现,通常使用效率更加稳定的Dijkstra算法。实际上dijkstra算法+heap优化后是一定快于一般SPFA的,而且更加稳定。

    1)SPFA的SLF优化,(很简单的,只要使用双端队列就可以实现了)。

    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <deque>
    using namespace std;
    const int N=501;
    const int NN=100001;
    const int inf=0x7fffffff;
    int n,nu;
    typedef struct node
    {
        int adj,val;
        struct node *next;
    };
    node node[NN],*p[N];
    int SPFA()
    {
        deque<int> qu;
        int x,i,a,b;
        int vis[N],dis[N],num[N];
        struct node *head[N];
        for(i=1;i<=n;i++)
        {
            vis[i]=0;
            num[i]=0;
            dis[i]=inf;
            head[i]=p[i];
        }
        dis[1]=0;
        vis[1]=1;
        num[1]++;
        qu.push_back(1);
        while(!qu.empty())
        {
            x=qu.front();
            qu.pop_front();
            vis[x]=0;
            head[x]=p[x];
            while(head[x])
            {
                a=head[x]->adj;
                b=head[x]->val;
                if(dis[a]>dis[x]+b)
                {
                    dis[a]=dis[x]+b;
                    if(!vis[a])
                    {
                        vis[a]=1;
                        num[a]++;
                        if(num[a]>=n)
                            return 1;
                        if(!qu.empty())
                        {
                            if(dis[a]>dis[qu.front()])
                                qu.push_back(a);
                            else
                                qu.push_front(a);
                        }
                        else
                            qu.push_back(a);
                    }
                }
                head[x]=head[x]->next;
            }
        }
        return 0;
    }
    int main()
    {
        int t,i,m,w,a,b,c;
        scanf("%d",&t);
        while(t--)
        {
            memset(node,0,sizeof(node));
            memset(p,0,sizeof(p));
            nu=0;
            scanf("%d%d%d",&n,&m,&w);
            for(i=0;i<m;i++)
            {
                scanf("%d%d%d",&a,&b,&c);
                node[nu].adj=b;
                node[nu].val=c;
                node[nu].next=p[a];
                p[a]=&node[nu];
                nu++;
                node[nu].adj=a;
                node[nu].val=c;
                node[nu].next=p[b];
                p[b]=&node[nu];
                nu++;
            }
            for(i=0;i<w;i++)
            {
                scanf("%d%d%d",&a,&b,&c);
                node[nu].adj=b;
                node[nu].val=-c;
                node[nu].next=p[a];
                p[a]=&node[nu];
                nu++;
            }
            if(SPFA())
                puts("YES");
            else
                puts("NO");
        }
        return 0;
    }
    网上找的
    #include<iostream>
    using namespace std;
    #include<deque>
    #include<cstdio>
    #include<cstring>
    int n,m;
    #define N 1001
    struct Edge{
        int u,v,w,last;
    }edge[N];
    int head[N];
    int dict[N];
    bool visit[N];
    void input()
    {
        scanf("%d%d",&n,&m);/*n个点,m条边*/
        for(int i=1;i<=m;++i)
        {
            scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
            edge[i].last=head[edge[i].u];
            head[edge[i].u]=i;
        }    
    }
    bool SPFA()
    {
        deque<int>q;
        memset(dict,127,sizeof(dict));
        memset(visit,false,sizeof(visit));
        dict[1]=0;
        visit[1]=true;;
        int cnt[N];
        memset(cnt,0,sizeof(cnt));/*判断有无环的标志*/
        ++cnt[1];
        while(!q.empty())
        {
            int k=q.front();
            q.pop_front();
            visit[k]=false;/*取出后,不要忘记标志位*/
            for(int l=head[k];l;l=edge[l].last)/*边表*/
            {
                int p=edge[l].v;
                if(dict[p]>dict[k]+edge[l].w)
                {
                    dict[p]=dict[k]+edge[l].w;
                    ++cnt[p];
                    if(cnt[p]>n) return true;/*如果某个点的入队次数超过了n,那么一定存在环*/
                    if(!visit[p])
                    {
                        visit[p]=true;
                        if(!q.empty())/*这就是SLF Small Label First 策略.的核心,把将要入队的元素的dict与队首元素相比较,如果将要入队的元素的dict大的话,就放在队尾,否则就放在队首 */
                        {
                            if(dict[p]>dict[q.front()])/*这样可以保证始终用dict小的更新,也节约了时间*/
                            q.push_back(p);
                            else q.push_front(p);/*所以必须使用双端队列*/
                        }
                        else q.push_back(p);/*不要忘记考虑队列为空的情况*/
                    }
                }
            }
        }
        return false;
    }
    int main()
    {
        input();
        if(SPFA())
        printf("circle");
        else{
            for(int i=1;i<=n;++i)
            printf("%d ",dict[i]);
        }
        return 0;
    }
    自己打了一遍,还可以理解

    2.SPFA的LLL优化

       在网上实在没找到可靠的。

    3.SPFA的DFS优化:

       在很多的题目中,SPFA都是用BFS来实现的,对于随机图来说,BFS的速度会远大于DFS,但是对于某些特殊的图结构来说,DFS也是一个很好的选择

        例如 1):题目中要求在最短的时间内,判断有无环,DFS明显会比BFS快(例题是POj上一个判断单词接龙的题目)

              2):对于网格型的图,DFS的速度比BFS快

              模板:  

    void SPFA(int k)
    {
        flag[k]=true;
        for(int l=head[k];l;l=edge[l].last)
        {
            int v=edge[l].v;/*找出第一个可以被更新的点*/
            if(dis[v]>dis[k]+edge[l].w)
            {
                dis[v]=dis[k]+edge[l].w;
                if(!flag[v])
                {
                    SPFA(v);/*接着深搜下去*/
                }
                else /*这表明从某个点开始DFS,结果又搜到了这个点,说明存在负权回路*/
                {
                    printf("cycle");
                    return;
                }
            }
        }
        flag[k]=false;/*不要忘记再把k设为false,为的是让他能够重复入队*/
    }

    四,Floyd算法模板(没有任何优化,就是邻接矩阵和n^3,一般情况单源最短路是绝对不会用的)

    for(int k=1;k<=n;++k)/*注意这个k必须在最外层循环才行*/
    
              for(int i=1;i<=n;++i)
    
                 for(int j=1;j<=n;++j)
    
                  dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]); 

      

  • 相关阅读:
    echarts常用说明
    ionic4打包和ngix配置
    浏览器onbeforeunload
    vue中keepAlive的使用
    面试题
    git使用
    git常用命令
    ajax原理
    关于npm(一)
    React、Vue、Angular对比 ---- 新建及打包
  • 原文地址:https://www.cnblogs.com/c1299401227/p/5401240.html
Copyright © 2020-2023  润新知