最短路总结

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
原文地址:https://www.cnblogs.com/kongbursi-2292702937/p/11623928.html