最短路总结
Posted kongbursi-2292702937
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最短路总结相关的知识,希望对你有一定的参考价值。
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 }
例题:UVA10048
Dijkstra算法:
算法过程:
给你n个点m条边,你要求x到y的最短距离。这个时候你首先用从x为起点的边来处理一遍控制最短距离的数组dis[](数组初始化dis[x]=0,其他都是正无穷)。然后再用距离x最近的点(设为x1),用以x1为起点的边在来处理一遍dis数组。这个时候dis[x1]里面的值就是x->x1的最短距离。原因就是在所有边都是正数的情况下,那么肯定不可能从x通过一个第三者中转导致x->x1的距离变得更短
是一个单源最短路算法
优点:普通算法复杂度:O(N*N),加堆优化:O(N*logN)
缺点:不能处理负权边
贝西在田野里,她想回到谷仓,在农夫约翰早上叫醒她挤奶之前尽可能多睡一会儿。贝西需要美容觉,所以她想尽快回来。农夫约翰的田里有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 }
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是常数。可处理负边,可判断负环
仍以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 }
链接表代码:
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 }
以上是关于最短路总结的主要内容,如果未能解决你的问题,请参考以下文章