• 最短路大汇总


    松弛操作:当dis[i] > dis[j] + g[j][i]时 dis[i] = dis[j] + g[j][i]


    dijkstra 

    单源最短路 O(n^2)

    本质是贪心 不能处理负边

    分成两个集合 用vis标记 一个是已经找到最短路的一个是没有找到最短路的

    从已经找到最短路里的点出发,进行松弛操作 找到其中最小的加入最短路的集合中 再从这个点接下去更新,做n-1次

    所有的距离初始化为inf

    #include<stdio.h>  
    const int N=100;  
    const int INF=100000;              //INF假定为无穷大  
    int p[N][N],d[N];                  //p表示各节点间的距离,不存在路径即为无穷大;d表示从出发节点到各节点的最短路径长度  
      
    void dijkstra(int sec,int n)       //sec为出发节点,n为图中的节点数  
    {  
        int i,j,min,min_num;  
        int vis[N]={0,};               //用于标记是否已作为过中途节点,0表示没有,1表示有  
        for(i=0;i<n;i++)               //初始化  
        {  
            d[i]=p[sec][i];  
        }  
        vis[sec]=1;d[sec]=0;           //出发节点到自己的距离永远为0  
        for(i=1;i<n;i++)  
        {  
            min=INF;  
            for(j=0;j<n;j++)           //每次循环取出d数组中的未被作为过中途节点且数值最小的  
            {  
                if(!vis[j]&&d[j]<min)  
                {  
                    min=d[j];          //更新最小值  
                    min_num=j;         //更新最小值所对应的节点,即记录下标  
                }  
            }  
            vis[min_num]=1;            //标记该节点,表示其已被作为中途节点  
            for(j=0;j<n;j++)           //循环,经过min_num节点到达是否有更小距离,如有更小距离则更新d数组  
            {  
                if(d[j]>min+p[min_num][j])  
                {  
                    d[j]=min+p[min_num][j];  
                }  
            }  
        }  
    }  
    int main()  
    {  
        int i,j,n=5;                   //n表示图中的节点个数  
        for(i=0;i<n;i++)               //程序用二维数组p存储各节点间的距离,这里则进行初始化  
        {  
            for(j=0;j<n;j++)  
            {  
                p[i][j]=(i==j?0:INF);  //初始化:i到j路径为无穷大或者i到i本身为0  
            }  
        }  
        p[0][1]=10;p[0][3]=30;p[0][4]=100;p[1][2]=50;p[2][4]=50;p[3][2]=20;p[3][4]=60;  //p[i][j]表示节点i到节点j的距离  
        dijkstra(0,n);                 //求从节点0出发到各节点的最短距离  
        for(i=0;i<n;i++)               //打印从节点0出发到各节点的最短距离  
        {  
            printf(i==n-1?"%d
    ":"%d ",d[i]);  
        }  
        return 0;  
    }  
    记录路径:通过path数组来记录路径,path[i]=j表明节点i取得最小路径时,其最后一段走的是节点j到节点i。

    然后用栈回溯

    可以使用优先队列优化dijkstra, 让权值小的先出列,自定义比较器。使用pair 将结点和权值一起存入队列


    Bellman-Ford

    单源最短路 可以处理负边 O(mn)

    初始:dist(1)[u]=edge[v0,u],v0是源点 

    递推:dist(k)[u]=min{dist(k-1)[u],min{dist(k-1)[j]+edge[j,u]}} j=0,1,…,n-1,j<>u; k=2,3,4,…,n-1 。 

    dist(i)[u]为从源点v0出发最多经过不构成负权值回路的i条边到达终点u的最短路径长度

    n-1次过后便可以得到最短路,如果n-1次后依旧可以更新,那么说明存在负权回路或者是正权回路。 

    Bellman-ford算法有一个小优化:每次松弛先设一个标识flag,初值为FALSE,若有边更新则赋值为TRUE,最终如果还是FALSE则直接成功退出

    for(int k=1;k<=n-1;k++)//遍历点的次数  
       {  
           for(int i=1;i<=m;i++)//遍历边的次数  
           {  
               if(dis[v[i]]>dis[u[i]]+w[i])//如果从u到v的距离能够通过w这条边压缩路径 就要进行松弛操作  
               {  
                   dis[v[i]]=dis[u[i]]+w[i];  
               }  
           }  
       }  
    
    



    spfa

    bellman的队列优化

    算法思想:我们用数组d记录每个结点的最短路径估计值,用邻接表来存储图G。我们采取的方法是动态逼近法:设立一个先进先出的队列用来保存待优化的结点,优化时每次取出队首结点u,并且用u点当前的最短路径估计值对离开u点所指向的结点v进行松弛操作,如果v点的最短路径估计值有所调整,且v点不在当前的队列中,就将v点放入队尾。这样不断从队列中取出结点来进行松弛操作,直至队列空为止

    期望的时间复杂度O(ke), 其中k为所有顶点进队的平均次数,可以证明k一般小于等于2。

    如果某个点进入队列的次数超过N次则存在负环(SPFA无法处理带负环的图)

    void spfa(int s)
    {
        for(int i = 1; i <= n; i++){
            dis[i] = inf;
            vis[i] = false;
        }
        dis[s] = 0;
        vis[s] = true;
        queue<int> que;
        que.push(s);
        int i, v;
        while(!que.empty()){
            v = que.front();que.pop();
            vis[v] = false;
            for(i = 1; i <= n; i++){
                if(a[v][i] > 0 && dis[i] > dis[v] + a[v][i]){
                    dis[i] = dis[v] + a[v][i];
                    if(vis[i] == 0){
                        que.push(i);
                        vis[i] = true;
                    }
                }
            }
        }
    
    }

    1. SPFA算法有两个优化算法 SLF 和 LLL:
    2. SLF:Small Label First 策略,设要加入的节点是j,队首元素为i,若dist(j)<dist(i),则将j插入队首,
    3. 否则插入队尾。
    4. LLL:Large Label Last 策略,设队首元素为i,队列中所有dist值的平均值为x,若dist(i)>x则将i插入
    5. 到队尾,查找下一元素,直到找到某一i使得dist(i)<=x,则将i出对进行松弛操作。
    可用来解决查分约束问题:将不等式转化为最短路问题【POJ3159 Candies】


    Floyd

    任意两点最短路 O(n^3)  可处理负边

    对于稀疏的图,采用

    茂密的图,可以使用

    Floyd

    算法。

    对于稀疏的图,采用n次Dijkstra比较出色,对于茂密的图,可以使用Floyd算法。

    i到j的最短距离不外乎存在经过i与j之间的k和不经过k两种可能,所以可以令k=1,2,3,...,n(n是城市的数目),在检查d(ij)与d(ik)+d(kj)的值;在此d(ik)与d(kj)分别是目前为止所知道的i到k与k到j的最短距离,因此d(ik)+d(kj)就是i到j经过k的最短距离。所以,若有d(ij)>d(ik)+d(kj),就表示从i出发经过k再到j的距离要比原来的i到j距离短,自然把i到j的d(ij)重写为d(ik)+d(kj),每当一个k查完了,d(ij)就是目前的i到j的最短距离。重复这一过程,最后当查完所有的k时,d(ij)里面存放的就是i到j之间的最短距离了。

    for(int k=0; k<n; k++)//k一定要在最外层

    for(int i=0; i<n; i++) 

    for(int j=0; j<n; j++) 
        这样作的意义在于固定了k,把所有i到j而经过k的距离找出来,然后象开头所提到的那样进行比较和重写,因为k是在最外层的,所以会把所有的i到j都处理完后,才会移动到下一个k,这样就不会有问题了


    输出路径:

    p(ij)的值如果为p,就表示i到j的最短行经为i->...->p->j,也就是说p是i到j的最短行径中的j之前的最后一个城市

    一旦发现d(ij)>d(ik)+d(kj),就把p(kj)存入p(ij)

    n

    tra,对于

    茂密的图,可以使用

    Floyd

    算法。

    小技巧:

    稀疏图使用邻接表存储

  • 相关阅读:
    sklearn linear_model,svm,tree,naive bayes,ensemble
    便利过滤
    js下载
    小程序修改radio的大小
    el-tree 问题与需求
    jsp页面用html引入vue.js注意问题
    WebPack
    yarn
    vue-cli 4以上 vue.config.js
    Cannot find module 'core-js/modules/es6.regexp.constructor'
  • 原文地址:https://www.cnblogs.com/wyboooo/p/9643430.html
Copyright © 2020-2023  润新知