四种常用最短路径算法模板
2015-02-05 16:30
246 查看
最短路径算法中,有四种算法是最常见的,分别是Dijkstra算法,Floyd算法,Bellman-Ford算法和SPFA算法。
Dijkstra算法,求单源最短路径最稳定的一个算法,算法复杂度为O(n2),但可以通过队列优化。下面列出的模板是最原始的Dijkstra算法。以需要求的源为中心,向四周扩散,第一次求出的是与源直接相连接的点的距离。求出这些距离中的最短距离,然后通过这个点将与它相连接的点的最短距离更新,然后再求出现在的最短距离,如此这样下去,直到所有的点都已经被遍历过为止。已经求出最短距离的点不在参与更新。具体模板如下(以POJ3268为例):
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <vector>
#include <map>
using namespace std;
#define INF 0x7ffffff
#define eps 1e-8
#define sgn(a) (a>eps)-(a<-eps)
#define LL long long
#define out(v) cerr << #v << ": " << (v) << endl
#define SZ(v) ((int)(v).size())
const int maxint = -1u>>1;20 int n,m,x;
const int maxn = 1111;
int dist[maxn];
int dist2[maxn];
int d[maxn][maxn];
bool flag[maxn];
void Dijkstra(int x)
{
memset(flag,false,sizeof(flag));
for(int i=1;i<=n;++i)
{
dist[i] = d[x][i];
}
flag[x] = true;
dist[x] = 0;
for(int i=2;i<=n;++i)
{
//寻找没有标记而且dist值最小的点
int u = 1;
int mindis = maxint;
for(int j=1;j<=n;++j)
{
if(!flag[j] && dist[j] < mindis)
{
mindis = dist[j];
u = j;
}
}
flag[u] = true;
for(int j=1;j<=n;++j)
{
if(!flag[j] && d[u][j] < maxint)
{
dist[j] = min(dist[j],dist[u] + d[u][j]);
}
}
}
}
int main()
{
while(scanf("%d%d%d",&n,&m,&x) != EOF)
{
int a,b,len;
for(int i=1;i<=n;++i)
{
for(int j=1;j<=n;++j)
{
d[i][j] = maxint;
}
}
for(int i=1;i<=m;++i)
{
scanf("%d%d%d",&a,&b,&len);
d[a][b] = len;
}
Dijkstra(x);
for(int i=1;i<=n;++i) dist2[i] = dist[i];
for(int i=1;i<=n;++i)
{
for(int j=i+1;j<=n;++j)
{
swap(d[i][j],d[j][i]);
}
}
Dijkstra(x);
int ans = 0;
for(int i=1;i<=n;++i)
{
ans = max(ans,dist[i] + dist2[i]);
}
printf("%d\n",ans);
}
return 0;
}
Floyd算法其实是Floyd-Warshall算法的简称。分以下两步进行。
1,从任意一条单边路径开始。所有两点之间的距离是边的权,如果两点之间没有边相连,则权为无穷大。
2,对于每一对顶点 u 和 v,看看是否存在一个顶点 w 使得从 u 到 w 再到 v 比已知的路径更短。如果是更新它。
Floyd算法是一种动态规划算法,稠密图效果最佳,边权可正可负。此算法简单有效,由于三重循环结构紧凑,对于稠密图,效率要高于执行|V|次Dijkstra算法。
具体模板如下所示(以POJ2240为例):
Bellman-Ford算法
1、以下操作循环执行至多n-1次,n为顶点数:
2、对于每一条边e(u, v),如果Distant[u] + w(u, v) < Distant[v],则另Distant[v] = Distant[u]+w(u, v)。w(u, v)为边e(u,v)的权值;
若上述操作没有对Distant进行更新,说明最短路径已经查找完毕,或者部分点不可达,跳出循环。否则执行下次循环;
3、为了检测图中是否存在负环路,即权值之和小于0的环路。对于每一条边e(u, v),如果存在Distant[u] + w(u, v) < Distant[v]的边,则图中存在负环路,即是说改图无法求出单源最短路径。否则数组Distant
中记录的就是源点s到各顶点的最短路径长度。
具体模板如下所示:
SPFA算法其实就是Bellman-Ford算法,只是它用队列进行了优化。用队列进行优化有三种形式:
1、简单地用队列进行存储。
2、SLF:Small Label First 策略,设要加入的节点是j,队首元素为i,若dist(j)<dist(i),则将j插入队首,否则插入队尾。
3、LLL:Large Label Last 策略,设队首元素为i,队列中所有dist值的平均值为x,若dist(i)>x则将i插入到队尾,查找下一元素,直到找到某一i使得dist(i)<=x,则将i
出对进行松弛操作。
以下模板是针对第一种情况(POJ3268为例):
Dijkstra算法,求单源最短路径最稳定的一个算法,算法复杂度为O(n2),但可以通过队列优化。下面列出的模板是最原始的Dijkstra算法。以需要求的源为中心,向四周扩散,第一次求出的是与源直接相连接的点的距离。求出这些距离中的最短距离,然后通过这个点将与它相连接的点的最短距离更新,然后再求出现在的最短距离,如此这样下去,直到所有的点都已经被遍历过为止。已经求出最短距离的点不在参与更新。具体模板如下(以POJ3268为例):
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <vector>
#include <map>
using namespace std;
#define INF 0x7ffffff
#define eps 1e-8
#define sgn(a) (a>eps)-(a<-eps)
#define LL long long
#define out(v) cerr << #v << ": " << (v) << endl
#define SZ(v) ((int)(v).size())
const int maxint = -1u>>1;20 int n,m,x;
const int maxn = 1111;
int dist[maxn];
int dist2[maxn];
int d[maxn][maxn];
bool flag[maxn];
void Dijkstra(int x)
{
memset(flag,false,sizeof(flag));
for(int i=1;i<=n;++i)
{
dist[i] = d[x][i];
}
flag[x] = true;
dist[x] = 0;
for(int i=2;i<=n;++i)
{
//寻找没有标记而且dist值最小的点
int u = 1;
int mindis = maxint;
for(int j=1;j<=n;++j)
{
if(!flag[j] && dist[j] < mindis)
{
mindis = dist[j];
u = j;
}
}
flag[u] = true;
for(int j=1;j<=n;++j)
{
if(!flag[j] && d[u][j] < maxint)
{
dist[j] = min(dist[j],dist[u] + d[u][j]);
}
}
}
}
int main()
{
while(scanf("%d%d%d",&n,&m,&x) != EOF)
{
int a,b,len;
for(int i=1;i<=n;++i)
{
for(int j=1;j<=n;++j)
{
d[i][j] = maxint;
}
}
for(int i=1;i<=m;++i)
{
scanf("%d%d%d",&a,&b,&len);
d[a][b] = len;
}
Dijkstra(x);
for(int i=1;i<=n;++i) dist2[i] = dist[i];
for(int i=1;i<=n;++i)
{
for(int j=i+1;j<=n;++j)
{
swap(d[i][j],d[j][i]);
}
}
Dijkstra(x);
int ans = 0;
for(int i=1;i<=n;++i)
{
ans = max(ans,dist[i] + dist2[i]);
}
printf("%d\n",ans);
}
return 0;
}
Floyd算法其实是Floyd-Warshall算法的简称。分以下两步进行。
1,从任意一条单边路径开始。所有两点之间的距离是边的权,如果两点之间没有边相连,则权为无穷大。
2,对于每一对顶点 u 和 v,看看是否存在一个顶点 w 使得从 u 到 w 再到 v 比已知的路径更短。如果是更新它。
Floyd算法是一种动态规划算法,稠密图效果最佳,边权可正可负。此算法简单有效,由于三重循环结构紧凑,对于稠密图,效率要高于执行|V|次Dijkstra算法。
具体模板如下所示(以POJ2240为例):
/* * Author: xiagenyuan * Created Time: 2013/5/1 21:03:44 * File Name: D:\ACMICPC\20130501\POJ2240.cpp */ #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <string> #include <algorithm> #include <queue> #include <vector> #include <map> using namespace std; #define eps 1e-8 #define sgn(a) (a>eps)-(a<-eps) #define LL long long const int maxint = -1u>>1; const int maxn = 33; int n,m; map<string,int> mp;//用来为名字是字符串的点对应数字 double ra[maxn][maxn]; //存取两点间的的路径 void Floyd()//对临接表进行Floyd处理 { for(int k=1;k<=n;++k) { for(int i=1;i<=n;++i) { for(int j=1;j<=n;++j) { if(ra[i][j] < ra[i][k]*ra[k][j]) { ra[i][j] = ra[i][k]*ra[k][j]; } } } } } int main() { int cas = 1; while(scanf("%d",&n) != EOF && n) { mp.clear(); string name; for(int i=1;i<=n;++i) { cin>>name; mp[name]=i; } scanf("%d",&m); string name1,name2; double rate; memset(ra,1,sizeof(ra)); for(int i=1;i<=m;++i) { cin>>name1>>rate>>name2; ra[mp[name1]][mp[name2]]= rate; } Floyd(); bool flag = false; for(int i=1;i<=n;++i) { if(ra[i][i] > 1) { flag = true; break; } } if(flag) printf("Case %d: Yes\n",cas); else printf("Case %d: No\n",cas); cas++; } return 0; }
Bellman-Ford算法
1、以下操作循环执行至多n-1次,n为顶点数:
2、对于每一条边e(u, v),如果Distant[u] + w(u, v) < Distant[v],则另Distant[v] = Distant[u]+w(u, v)。w(u, v)为边e(u,v)的权值;
若上述操作没有对Distant进行更新,说明最短路径已经查找完毕,或者部分点不可达,跳出循环。否则执行下次循环;
3、为了检测图中是否存在负环路,即权值之和小于0的环路。对于每一条边e(u, v),如果存在Distant[u] + w(u, v) < Distant[v]的边,则图中存在负环路,即是说改图无法求出单源最短路径。否则数组Distant
中记录的就是源点s到各顶点的最短路径长度。
具体模板如下所示:
/* * Author: xiagenyuan * Created Time: 2013/5/1 21:39:36 * File Name: C:\Users\Genyuan\Desktop\图论系列模板\Bellman-Ford.cpp */ //模板未进行验证 #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <string> #include <algorithm> #include <queue> #include <vector> #include <map> using namespace std; #define eps 1e-8 #define sgn(a) (a>eps)-(a<-eps) #define LL long long const int maxint = 9999999; const int maxnum = 100; struct edge { int u,v;//每条边的起点和终点 int weight;//边的权值 }; edge e[maxnum];//保存所有边的值 int dist[maxnum]; //保存节点到源点的最短距离 int n,m,x; //节点数量,边的数量,源点 //读入数据,初始化图 void init() { while(scanf("%d%d%d",&n,&m,&x) != EOF) { for(int i=1;i<=n;++i) dist[i] = maxint; dist[x] = 0; for(int i=1;i<m;++i) { scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].weight); if(e[i].u == x) dist[e[i].v] = e[i].weight; } } } //松弛计算 void relax(int u,int v,int weight) { dist[v] = min(dist[v],dist[u]+weight); } bool BellmanFord() { for(int i=1;i<n-1;++i) { for(int j=1;j<=m;++j) { relax(e[j].u,e[j].v,e[j].weight); } } bool flag = true; for(int i=1;i<m;++i) { if(dist[e[i].v] > dist[e[i].u]+e[i].weight) { flag = false; break; } } return flag; } int main() { init(); if(BellmanFord()) { for(int i=1;i<=m;++i) cout<<dist[i]<<" "; cout<<endl; } else cout<<"No"<<endl; return 0; }
SPFA算法其实就是Bellman-Ford算法,只是它用队列进行了优化。用队列进行优化有三种形式:
1、简单地用队列进行存储。
2、SLF:Small Label First 策略,设要加入的节点是j,队首元素为i,若dist(j)<dist(i),则将j插入队首,否则插入队尾。
3、LLL:Large Label Last 策略,设队首元素为i,队列中所有dist值的平均值为x,若dist(i)>x则将i插入到队尾,查找下一元素,直到找到某一i使得dist(i)<=x,则将i
出对进行松弛操作。
以下模板是针对第一种情况(POJ3268为例):
/* * Author: xiagenyuan * Created Time: 2013/5/1 22:26:23 * File Name: D:\ACMICPC\20130501\POJ3268SPFA.cpp */ #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <string> #include <algorithm> #include <queue> #include <vector> #include <map> using namespace std; #define eps 1e-8 #define sgn(a) (a>eps)-(a<-eps) #define LL long long const int maxint = 99999999; const int maxn = 1000 + 111; int n,m,x; int d[maxn][maxn]; int dist[maxn]; int dist2[maxn]; bool visited[maxn]; int que[2*maxn]; void spfa() { int pri = 0,end = 1; memset(visited,false,sizeof(visited)); visited[x] = true; for(int i=1;i<=n;++i) dist[i] = maxint; dist[x] = 0; que[0] = x; while(pri < end) { int index = que[pri]; for(int i=1;i<=n;++i) { if(dist[index] + d[index][i] < dist[i]) { dist[i] = dist[index] + d[index][i]; if(!visited[i]) { que[end++] = i; visited[i] = true; } } } visited[index] = false; pri++; } } int main() { while(scanf("%d%d%d",&n,&m,&x) != EOF) { int a,b,len; for(int i=1;i<=n;++i) { for(int j=1;j<=n;++j) { d[i][j] = maxint; } } for(int i=1;i<=m;++i) { scanf("%d%d%d",&a,&b,&len); d[a][b] = len; } spfa(); for(int i=1;i<=n;++i) dist2[i] = dist[i]; for(int i=1;i<=n;++i) { for(int j=i+1;j<=n;++j) { swap(d[i][j],d[j][i]); } } spfa(); int ans = 0; for(int i=1;i<=n;++i) { ans = max(ans,dist[i] + dist2[i]); } printf("%d\n",ans); } return 0; }
相关文章推荐
- 四种常用最短路径算法模板
- 四种常用最短路径算法模板
- 最短路径 Dijkstra算法(模板)
- 最短路径问题(算法模板)
- 模板_Dijkstra最短路径算法
- 图论(二):图的四种最短路径算法
- dijkstra 最短路径算法模板
- 单源最短路径算法模板(Dijkstra+BellmanFrod)
- 【2018寒假集训Day 7】【最短路径】三种算法的模板
- (最短路径算法整理)dijkstra、floyd、bellman-ford、spfa算法模板的整理与介绍
- 九度 OJ 1447 题 最短路径问题 Dijkstra(迪杰斯特拉)算法实现,使用vector模板模拟邻接链表
- 数据结构——图常用算法实现(DFS,BFS,最小生成树,最短路径,拓扑序列)
- 最短路径—基于邻接矩阵的的Dijstra算法模板(不输出路径)
- 最短路径算法 模板_Dijkstra_Bellman.ford_Floyd_spfa
- 四种最短路径算法(Dijkstra,Floyd,Bellman-ford&&spfa)
- 关于图的常用算法——Dijkstra单源最短路径、Floyd多源最短路径、Prim和Kruskal最小生成树算法
- 最短路径常用算法总结
- 单源最短路径算法模板(Dijkstra+BellmanFrod)
- (最短路径算法整理)dijkstra、floyd、bellman-ford、spfa算法模板的整理与介绍
- 四种最短路径算法对比