• 最短路总结


    Floyd算法:

     复杂度O(n^3)

    首先这个算法使用暴力dp来写的,很容易就会TLE。但是这是一个多源最短路算法,可以求出来任意两点之间的最短距离

    示例代码:

     1 #include <cstdio>
     2 #include <iostream>
     3 #define INF 0x3f3f3f3f
     4 using namespace std;
     5 int n,m,t,a,b,c;
     6 int f[105][105];
     7 int main()
     8 {
     9     //有n个端点,m条边 
    10     cin >> n >> m >> t;
    11     //初始化 
    12     for (int i=1;i<=n;i++)
    13         for (int j=1;j<=n;j++)
    14         {
    15             if (i==j) f[i][j]=0;
    16             else f[i][j]=INF;
    17         }
    18     //输入边 
    19     for (int i=1;i<=m;i++)
    20     {
    21         scanf("%d%d%d",&a,&b,&c);
    22         f[a][b]=c;
    23     }
    24     //核心代码 
    25     for (int k=1;k<=n;k++)
    26         for (int i=1;i<=n;i++)
    27             for (int j=1;j<=n;j++)
    28                 f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
    29     //运行完算法之后,f[x][y]里面就是x与y之间的最短距离 
    30     return 0;
    31 }
    View Code

    例题:UVA10048

    Dijkstra算法:

    算法过程:

    给你n个点m条边,你要求x到y的最短距离。这个时候你首先用从x为起点的边来处理一遍控制最短距离的数组dis[](数组初始化dis[x]=0,其他都是正无穷)。然后再用距离x最近的点(设为x1),用以x1为起点的边在来处理一遍dis数组。这个时候dis[x1]里面的值就是x->x1的最短距离。原因就是在所有边都是正数的情况下,那么肯定不可能从x通过一个第三者中转导致x->x1的距离变得更短

    堆优化迪杰斯特拉优化的哪个地方:没有堆优化的时候我们用的是普通队列,在以x为起点的时候我们对所有与x有关的边都进行一次松弛(比如有x-->1这一条边,我们要去判断dis[1]与dis[x]+w[x][1],w[x][1]是x与1这一条边的长度),所有边进行松弛之后我们要找出来除了dis[x]之外最小的那个dis[y](这个y是我们假设的那个与x相距最小的那个点【同样这个y也是一个未确定过的点,什么叫未确定?比如x就叫确定过的点,因为dis[x]我们已经保证它是最小的了】)

    注意了:我们堆优化的就是这个找dis[y]的过程(因为暴力找需要for循环 ,而我们用优先队列了之后就不需要这么麻烦了)

    是一个单源最短路算法

    优点:

    普通算法复杂度:O(v*e),

    加堆优化:在核心代码部分,最复杂的是 while 循环和 for 循环嵌套的部分,while 循环最多循环 v 次(v 为顶点个数),for 循环执行次数与边的数目有关,假设顶点数 v 的最大边数是 e。
    for 循环中往优先队列中添加删除数据的复杂度为O(log v)。
    综合上述两部分,最终 Dijkstra 算法的时间复杂度是O(e·logv)

    缺点:不能处理负权边

    例题:Til the Cows Come Home

    贝西在田野里,她想回到谷仓,在农夫约翰早上叫醒她挤奶之前尽可能多睡一会儿。贝西需要美容觉,所以她想尽快回来。农夫约翰的田里有N个(2 <= N <= 1000)个界标,唯一的编号是1..地标是谷仓;贝茜整天站在里面的小树林是地标性的N.奶牛在田野里用T (1 <= T <= 2000)在地标之间不同长度的双向牛道。贝西对自己的导航能力不太自信,所以一旦她开始了,她总是沿着一条路线走到底。根据各地标之间的步道,确定贝西返回谷仓的最小距离。可以保证存在这样的路由。
    输入*第一行:两个整数:T和N
    第2行 . .T+1:每一行都用三个空格分隔的整数来描述一个轨迹。前两个整数是路线经过的地标。第三个整数是步道的长度,范围1..100。
    输出*第1行:单个整数,贝茜从地标N到地标1的最小距离。
    
    样例输入
    5 5
    1 2 20
    2 3 30
    3 4 20
    4 5 20
    1 5 100
    样例输出
    90

    加堆优化+代码:

     1 //Dijkstra迪杰斯特拉+堆优化(邻接矩阵存图)
     2 #include<stdio.h>
     3 #include<string.h>
     4 #include<iostream>
     5 #include<algorithm>
     6 #include<queue>
     7 #include<vector>
     8 using namespace std;
     9 #define MAX 0xffffff
    10 struct shudui1
    11 {
    12     int start,value;
    13     bool operator < (const shudui1 q)const
    14     {
    15         return value<q.value;
    16     }
    17 }str1;
    18 struct shudui2
    19 {
    20     int start,value;
    21 }str2;
    22 int v[1005];
    23 priority_queue<shudui1>r; //里面的数据默认是从小到大排序,这样就不用通过for循环遍历在每一次找v里面的最小值,可以直接找到最小值,减少代码运行次数
    24 int a,s,d,f,g;
    25 vector <shudui2>w[2005];  //将每一个起点的值作为数组的标识
    26             //例如,1 2 3这一组数据,就是说1连接着2,那就把所有1能到的地方存到这个里面
    27 int main()
    28 {
    29     scanf("%d%d",&a,&s);
    30     for(int i=1;i<=s;++i)
    31         v[i]=MAX;
    32     while(a--)
    33     {
    34         scanf("%d%d%d",&d,&f,&g);
    35         str2.start=f;
    36         str2.value=g;
    37         w[d].push_back(str2);
    38         str2.start=d;
    39         str2.value=g;
    40         w[f].push_back(str2);
    41     }
    42     v[s]=0;
    43     str1.start=s;
    44     str1.value=0;
    45     r.push(str1);
    46     while(!r.empty())
    47     {
    48         int x,y;
    49         str1=r.top();
    50         r.pop();
    51         x=str1.start;
    52         y=str1.value;
    53         if(v[x]<y) continue;
    54         //说明在这个点再此之后又入队了
    55         //此次出队的并不是s到这个点的最短路,
    56         //所以在这次更新前点v所连的点已经更过一次了
    57         //所以后面也不会进行松弛操作
    58         int len=w[x].size();
    59         for(int i=0;i<len;++i)
    60         {
    61             str2=w[x][i];
    62             if((v[x]+str2.value<v[str2.start]))
    63             {
    64                 v[str2.start]=v[x]+str2.value;
    65                 str1.start=str2.start;
    66                 str1.value=v[str2.start];
    67                 r.push(str1);
    68             }
    69         }
    70     }
    71     printf("%d
    ",v[1]);
    72     return 0;
    73 }
    View Code

    注意: 如果是多组输入,要清空vector容器呀!!!!!!!!!(wa的我还以为是我的模板错了。。。。)

     修正一下上面迪杰斯特拉中一部分代码:

    //Dijkstra迪杰斯特拉+堆优化(邻接矩阵存图)
    #include<stdio.h>
    #include<string.h>
    #include<iostream>
    #include<algorithm>
    #include<queue>
    #include<vector>
    using namespace std;
    #define MAX 0xffffff
    struct shudui1
    {
        int start,value;
        bool operator < (const shudui1 q)const
        {
            return value>q.value;   //这个要改成大于号
        }
    }str1;

    HDU-6290 奢侈的旅行 (Dijkstra+堆优化)    就例如这一道题,你用小于号就会wa

    Spay算法(bellman-Ford优化):

    算法过程:

    给你n个点m条边,你要求x到y的最短距离。这个时候你要枚举每一条边u->v,权值为w,看可不可以通过dis[v]=dis[u]+w来找到dis[v]的更小值(数组初始化dis[x]=0,其他都是正无穷)。在第一轮的松弛之后,得到的是“只经过一条边”到达其余各顶点的最短路径长度。第二轮的时候,得到的是“只经过二条边”到达其余各顶点的最短路径长度。那我们只需要经过n-1轮次的循环就可以了,因为在一个有n个顶点的图中,任意两点之间的距离最多包含n-1边

    Bellman-Ford这种写法相当暴力, 直接循环nodeNum次, 每次枚举每一条边, 假如这条边可以用于松弛源点到端点的距离, 则进行松弛. 至于判断负环, 再枚举一遍所有边, 假如存在边仍能用于松弛, 则说明存在负权回路.

    SPFA算法的优化体现在:松弛操作必定只会发生在最短路径前导节点松弛成功过的节点上,用一个队列记录松弛过的节点,可以避免了冗余计算。复杂度可以降低到O(kE),k是个比较小的系数(并且在绝大多数的图中,k<=2,然而在一些精心构造的图中可能会上升到很高)

    关于SPFA的时间复杂度,不好准确估计,一般认为是 OkE),k是常数。可处理负边,可判断负环

    Bellman-Ford算法代码:POJ 3259 Wormholes

      1 #include <stdio.h>
      2 
      3 #include <string.h>
      4 
      5 #define N 505
      6 
      7 #define inf 100000
      8 
      9 int dis[N];   //最小距离 
     10 
     11 int n, m;
     12 
     13 struct Cow 
     14 
     15 {
     16 
     17     int x;//起点
     18 
     19     int y;//终点
     20 
     21     int w;//权值
     22 
     23 }cow[5300];
     24 
     25 int Bellman_ford(int m)
     26 
     27 {
     28 
     29     int i, j, k, t;
     30 
     31     int ok;
     32 
     33     for(i=1; i<=n; i++)
     34 
     35         dis[i] = inf;
     36 
     37     dis[1] = 0;
     38 
     39     //对每一个点进行松弛   尽可能的让路径最小 
     40 
     41     for(i=1; i<=n-1; i++)
     42 
     43     {
     44 
     45         ok = 1;
     46 
     47         for(j=1; j<=m; j++)
     48 
     49         {
     50 
     51             if(dis[cow[j].x] > dis[cow[j].y] + cow[j].w)
     52 
     53             {
     54 
     55                 dis[cow[j].x] = dis[cow[j].y] + cow[j].w;
     56 
     57                 ok = 0;
     58 
     59             }
     60 
     61         } 
     62 
     63         //如果没有点进行更新  那么跳出即可  可减小时间复杂度 
     64 
     65         if(ok) 
     66 
     67             break; 
     68 
     69     }
     70 
     71     //判断图中是否有负权值存在 
     72 
     73     for(j=1; j<=m; j++)
     74 
     75     {
     76 
     77         if(d[cow[j].x] > d[cow[j].y] + cow[j].w)
     78 
     79             return 0;
     80 
     81     }
     82 
     83     return 1;
     84 
     85 }
     86 
     87  
     88 
     89 int main()
     90 
     91 {
     92 
     93     int i, j, t, x;
     94 
     95     int a, b, c, w;
     96 
     97     scanf("%d", &t);
     98 
     99     while(t--)
    100 
    101     {
    102 
    103         scanf("%d%d%d", &n, &m, &w);
    104 
    105         memset(edges, 0, sizeof(edges));
    106 
    107         //因为是无向图  所以需要将所有的添加进去 
    108 
    109         for(i=0, j=1; j<=m;j++)
    110 
    111         {
    112 
    113             scanf("%d%d%d", &a, &b, &c);
    114 
    115             i++;
    116 
    117             cow[i].x = a;
    118 
    119             cow[i].y = b;
    120 
    121             cow[i].w = c;
    122 
    123             i++;
    124 
    125             cow[i].x = b;
    126 
    127             cow[i].y = a;
    128 
    129             cow[i].w = c;
    130 
    131  
    132 
    133         }
    134 
    135         for(j=1; j<=w; j++)
    136 
    137         {
    138 
    139             scanf("%d%d%d", &a, &b, &c);
    140 
    141  
    142 
    143             i++;
    144 
    145             cow[i].x = a ;
    146 
    147             cow[i].y = b;
    148 
    149             cow[i].w = -c;
    150 
    151  
    152 
    153         }
    154 
    155         if(Bellman_ford(i))
    156 
    157             printf("NO
    ");
    158 
    159         else 
    160 
    161             printf("YES
    ");
    162 
    163     }
    164 
    165     return 0;
    166 
    167 }
    View Code

    具体看这里

    仍以Til the Cows Come Home 为例题:

     1 #include<stdio.h>
     2 #include<string.h>
     3 #include<queue>
     4 #include<algorithm>
     5 #include<iostream>
     6 using namespace std;
     7 const int INF =0xfffff;
     8 const int MAX =2005;
     9 int w[MAX][MAX];
    10 int d[MAX];
    11 int vis[MAX];
    12 queue<int>q;
    13 bool spay(int s,int n)   //这个可以用来判断有没有负环的存在
    14 {
    15     d[s]=0;
    16     int cnt=0;
    17     q.push(s);
    18     q.push(cnt);
    19     vis[s]=1;
    20     while(!q.empty())
    21     {
    22         int x=q.front();
    23         q.pop();
    24         cnt=q.front();
    25         q.pop();
    26         vis[x]=0;
    27         if(cnt>n) return 0;  //这个是用来判断有没有负环,没有负环的情况最多只用查询其n-1个顶点,多于n个那就不正常了
    28         for(int i=1;i<n;++i) //本体是从n点到其他点,所以n点不需要再回到n点,可以不遍历
    29         {
    30             if(d[i]>d[x]+w[x][i])
    31             {
    32                 d[i]=d[x]+w[x][i];
    33                 if(!vis[i])
    34                 {
    35                     q.push(i);
    36                     q.push(cnt+1);
    37                     vis[i]=1;
    38                 }
    39             }
    40         }
    41     }
    42     return 1;
    43 }
    44 int main()
    45 {
    46     int n,m;
    47     scanf("%d%d",&n,&m);
    48     memset(w, 0x3f, sizeof(w));
    49     for(int i=1;i<=m;++i)
    50         d[i]=INF;
    51     while(n--)
    52     {
    53         int a,s,d;
    54         scanf("%d%d%d",&a,&s,&d);
    55         if(w[a][s]>d)   //防止重边
    56         w[a][s]=d;
    57         if(w[s][a]>d)
    58         w[s][a]=d;
    59     }
    60     spay(m,m);
    61     printf("%d
    ",d[1]);
    62     return 0;
    63 }
    View Code

    链接表代码:

     1 #include <stdio.h>
     2 #include<string.h>
     3 #include <iostream>
     4 #define INF 0x3f3f3f3f
     5 using namespace std;
     6 const int maxn=1005;
     7 struct edge
     8 {
     9     int u,v,w,next;    
    10 }e[maxn*maxn];  //如果题目上没有说,那就是n*n条边,n是点 
    11 int head[maxn],cnt;
    12 void add_edge(int x,int y,int z){  //链接表 
    13     e[cnt].u=x;
    14     e[cnt].v=y;
    15     e[cnt].w=z;
    16     e[cnt].next=head[x];
    17     head[x]=cnt++;
    18 }
    19 int main(){
    20     cnt=0;
    21     int x,y,z;
    22     while(~scanf("%d%d%d",&x,&y,&z)){
    23         add_edge(x,y,z);
    24     }
    25     for(int i=0;i<cnt;++i) //打印 
    26     {
    27         printf("%d %d %d
    ",e[i].u,e[i].v,e[i].w);
    28     }
    29     return 0;
    30 } 
    View Code
  • 相关阅读:
    windows中administrator 和 administrators两个账户的区别
    如何去掉打印网页时自带的网址以及页码等内容
    Oracle左连接,右连接
    oracle服务器本地能够登录但是局域网内其他机器不能访问的解决方法
    错误Name node is in safe mode的解决方法
    oracle数据库中对varchar类型求max的解决方法
    JBoss中配置数据源出现错误:“Failed to register driver for: com.mysql.jdbc.Driver”的解决方法
    学习junit和hamcrest的使用
    Ubuntu10.10如何给用户添加sudo权限
    ORACLE 9i卸载并重新安装
  • 原文地址:https://www.cnblogs.com/kongbursi-2292702937/p/11623928.html
Copyright © 2020-2023  润新知