最短路(转)模板

基本最短路算法集锦

算法总结:

①Dijkstra算法用的是贪心策略,每次都找当前最短路径的下一个最短距离点。所以不适合带有负权的情况。至于时间效率通过各种优化可以到达不同的程度。但是朴素的Dijkstra算法永远是最稳定的。

②Bellman-Ford算法是Dijkstra的一种变式,它摒弃了贪心的策略,但是每次都需要松弛所有的路径,所以也适合负权的情况。但是时间效率较低。有资料显示,Bellman-Ford算法也可以应用贪心策略,这属于高级技巧,这里不予考虑。

③Floyd算法用的是动态规划的思想,由于是不定源点,所以它需要对所有的情况进行松弛操作,最终获得所有的最短路径长度。由于需要遍历,所以它的时间效率比较低。但是遍历策略好的话,还是能很快得到想要的结果。

④SPFA算法是用FIFO队列优化的Bellman-Ford算法,所以支持负权的情况,由于已经将需要考虑的节点放入队列中,所以避免了很多不必要的松弛,时间效率也相对较高。由于队列中无法进行修改,所以时间效率相对不稳定。

⑤所有的队列优化其实都能转化为hash优化,这里不再考虑更进一步的优化。

 

 

一、Dijkstra算法:单源到所有节点的最短路算法。

算法要求:可以是无向图或有向图,所有边权均为正,最短路存在。如果图是五环图,则最长路也一定存在,但是如果是有环图,则最长路不一定存在。

算法思想:每次比较所有的从源点可以到达的路的长度,然后选出最短的一条放入源点集合,然后更新最短路径长度。这样,当所有的点都在源点集合中时,得到的长度就是最短路长度。

(1)使用邻接矩阵的稠密图普通Dijkstra算法

算法时间复杂度:O(n^2)

算法代码:

#include <iostream>

#include <cstdio>

#include <cstring>

using namespace std;

#define INF (1<<31)-1   //int最大值

#define MAXN 100   //最大节点数

int dist[MAXN],c[MAXN][MAXN];

//dist[i]存放从源点到i节点的最短距离,c数组用来表示图

int n,line;   //n为节点数,line为边数

//迪杰斯特拉算法主算法模块

void Dijkstra(int v)   //v代表源点

{

inti,j,temp,u;  //temp用于暂时存放最短路径长度

boolvis[MAXN];   //判定重复标记数组

for(i=0;i<n;i++)   //dist数组与vis数组的初始化

{

dist[i]=c[v][i];

vis[i]=0;

}

dist[v]=0;  //源点到源点的距离设为0

vis[v]=1;   //源点默认为已经访问

for(i=0;i<n;i++)  //总共应该遍历n次

{

temp=INF;  //初始化temp为最大值

u=v;   //初始化最短节点为当前节点

for(j=0;j<n;j++)if((!vis[j])&&dist[j]<temp)

{

u=j;   //找出v距离最短的节点u

temp=dist[j];   //更新最短距离

}

if(temp==INF)return;   //不存在其它节点了,返回

vis[u]=1;   //标记该最短节点为已经访问

for(j=0;j<n;j++)if((!vis[j])&&dist[u]+c[u][v]<dist[j])  //松弛操作

dist[j]=dist[u]+c[u][v];  //更新最短距离

}

}

int main()

{

int p,q,len,i,j,origin;

while(scanf(“%d%d”,&n,&line))

{

if(!n&&!line) break;  //如果图为空白图,则结束

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

for(j=0;j<n;j++)

c[i][j]=INF;   //初始化节点间距离,默认为节点间全部不连通

while(line--)   //输入这line条边的内容

{

scanf(“%d%d%d”,&p,&q,&len);

//p为边的起点,q为边的终点,len为边的长度

//下面是无向图的输入方式

if(len<c[p][q])

{

c[p][q]=len;

c[q][p]=len;

}

/*下面是有向图的输入方式

if(len<c[p][q])

c[p][q]=lem;

以上是有向图的输入方式*/

}

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

dist[i]=INF;   //初始化最短距离数组为最大长度

scanf(“%d”,&origin);

Dijkstra(origin);  //最短路算法调用

printf(“%d\n”,dist[n]);

}

}

(2)使用邻接表的稀疏图的普通Dijkstra算法

算法时间复杂度:O(m*log(n))

算法代码:(代码以无向图为例,有向图部分以注释形式写在代码中)

###头文件以及宏定义部分省略###

int first[MAXN];  //first[u]保存节点u的第一条边的编号

int u[MAXN],v[MAXN],w[MAXN];

//u[i]表示第i条边的一端端点,v[i]表示第i条边的另一端端点,w[i]表示第i条边的长度

int next[2*MAXN];  //next[i]表示第i条边的下一条边的编号【无向图】

//int next[MAXN]; next[i]表示第i条边的下一条边的编号。【有向图】

#########省略部分#########

scanf(“%d%d”,&n,&m);

for(i=0;i<n;i++) first[i]=-1;   //初始化链表表头

for(i=0;i<2*line;i++)  //输入2*line条边,每次将边首插法插入链表表头(避免遍历链表)。

//如果是有向图,那么只有line条边,即i<line。

{

scanf(“%d%d%d”,&u[i],&v[i],&w[i]);

next[i]=first[u[i]];

first[u[i]]=i;

//以下只有无向图才有

i++;

u[i]=v[i-1];

v[i]=u[i-1];

next[i]=first[u[i]];

first[u[i]]=i;

//以上只有无向图才有

}

//对应的Dijkstra算法

void Dijkstra(int x)   //v代表源点

{

inti,j,temp,minu;  //temp用于暂时存放最短路径长度

boolvis[MAXN];   //判定重复标记数组

for(i=first[x];i!=-1;i=next[i])  //dist数组与vis数组的初始化

{

dist[v[i]]=w[i];

vis[v[i]]=0;

}

dist[x]=0;  //源点到源点的距离设为0

vis[x]=1;   //源点默认为已经访问

for(i=0;i<n;i++)  //总共应该遍历n次

{

temp=INF;  //初始化temp为最大值

minu=x;   //初始化最短节点为当前节点

for(j=first[x];j!=-1;j=next[j])

{

if((!vis[v[j]])&&dist[v[j]]<temp)

{

minu=j;   //找出v距离最短的节点uu

temp=dist[v[j]];   //更新最短距离

}

}

if(temp==INF)return;   //不存在其它节点了,返回

vis[minu]=1;   //标记该最短节点为已经访问

for(j=first[x];j!=-1;j=next[j])if((!vis[v[j]])&&dist[minu]+w[j]<dist[v[j]])  //松弛操作

dist[v[j]]=dist[minu]+w[j];  //更新最短距离

}

}

(3)使用优先队列的Dijkstra优化算法

算法时间复杂度:O(n*lgn+ m)。一般情况下最快。

算法代码:

#include <queue>   //需要优先队列

#include <utility>   //需要pair类型

######其他头文件已经宏定义省略######

int first[MAXN];  //first[u]保存节点u的第一条边的编号

int u[MAXN],v[MAXN],w[MAXN];

//u[i]表示第i条边的一端端点,v[i]表示第i条边的另一端端点,w[i]表示第i条边的长度

int next[2*MAXN];  //next[i]表示第i条边的下一条边的编号【无向图】

//int next[MAXN]; next[i]表示第i条边的下一条边的编号。【有向图】

#########省略部分#########

scanf(“%d%d”,&n,&m);

for(i=0;i<n;i++) first[i]=-1;   //初始化链表表头

for(i=0;i<2*line;i++)  //输入2*line条边,每次将边首插法插入链表表头(避免遍历链表)。

//如果是有向图,那么只有line条边,即i<line。

{

scanf(“%d%d%d”,&u[i],&v[i],&w[i]);

next[i]=first[u[i]];

first[u[i]]=i;

//以下只有无向图才有

i++;

u[i]=v[i-1];

v[i]=u[i-1];

next[i]=first[u[i]];

first[u[i]]=i;

//以上只有无向图才有

}

typedef pair<int,int> pii;  //定义双对象类型

priority_queue<pii,vector<pii>,greater<pii>> pq;   //声明最小出队的优先队列

//对应的Dijkstra算法

int Dijkstra(int x)

{

inti,j;

pq.push(make_pair(d[x],x);

while(!pq.empty())

{

piiminu=pq.top();pq.pop();

intx=minu.second;

if(minu.first!=dist[x])continue;

for(inti=first[x];i!=-1;i=next[i]) if(d[v[i]]>dist[x]+w[i])

{

dist[v[i]]=dist[x]+w[i];

pq.push(make_pair(d[v[i]],v[i]));

}

}

}

二、Bellman-Ford算法:单源到所有节点的最短最长路算法。

算法要求:可以使无向图或者有向图。边权可以存在负值。当然最短路不一定存在,当最短路存在时,可以求出最短路长度。如果图为有环图,则最短路不一定存在,最长路也不一定存在。如有负权则输出错误提示。也适合求解约束拆分系统的不等式问题。

算法思想:如果最短路存在,一定存在一个不含环的最短路。在边权可正可负的图中,环有零环、正环、负环3种。如果包含零环或正环,去掉以后路径不会变长;如果包含负环,则最短路不存在。可以通过n-1轮松弛操作得到。

(1)朴素的BF算法。

算法时间复杂度:O(mn)

算法代码:

const int N =205;

const int M =20005;

const int MAXN = 1000000000

int dist[N];

//自定义边的结构体

struct edge{int u,v,w;}e[M];  //u,v分别是边的两端点,w为边长度

//初始化dist数组

void init(int vs,int s)

{

inti;

for(i=0;i<vs;i++)

dist[i]=MAXN;

dist[s]=0;

return;

}

//松弛操作,成功返回true,失败返回false

bool relax(int u,int v,int w)

{

if(dist[v]>dist[u]+w)

{

dist[v]=dist[u]+w;

returntrue;

}

return false;

}

//BF主算法模块,返回false表示算法失败,图中存在负环。

bool bellmanFord(int es,int vs,int s)   //es表示边数,vs表示点数,s表示起点

{

inti,j;

init(vs,s);

boolflag;

for(i=0;i<vs-1;i++)   //应进行vs-1次松弛操作

{

flag=false;

for(j=0;j<es;j++)

if(relax(e[j].u,e[j].v,e[j].w))

flag=true;

return flag;

}

}

int main()

{

intn,m,i;

while(scanf(“%d%d”,&n,&m)!=EOF)

{

for(i=0;i<m;i++)

{

scanf(“%d%d%d”,&e[i].u,&e[i].v,&e[i].w);

e[m+i].u=e[i].v;

e[m+i].v=e[i].u;

e[m+i].w=e[i].w;

}

if(bellmanFord(m<<1,n,1))

printf(“%d\n”,dist[n]);

else printf(“No\n”);

}

return 0;

}

(2)使用FIFO队列的优化BF算法(使用邻接表)

算法时间复杂度:O(mn)

算法代码:

#include <queue>

#define INF (1<<31)-1

######其他头文件以及宏定义省略######

int first[MAXN];  //first[u]保存节点u的第一条边的编号

int u[MAXN],v[MAXN],w[MAXN];

//u[i]表示第i条边的一端端点,v[i]表示第i条边的另一端端点,w[i]表示第i条边的长度

int next[2*MAXN];  //next[i]表示第i条边的下一条边的编号【无向图】

//int next[MAXN]; next[i]表示第i条边的下一条边的编号。【有向图】

#########省略部分#########

queue<int> q;

bool inq[MAXN];

bool bellmanFord(int x)

{

int i,j;

bool ans;

for(i=0;i<n;i++)d[i]=!i?0:INF;

memset(inq,0,sizeof(inq));   //在队列中的标志

q.push(x);

ans=false;

while(!q.empty())

{

int x=q.front();q.pop();

inq[x]=false;

for(i=first[x];i!=-1;i=next[i]) if(dist[v[e]]>dist[x]+w[e])

{

dist[v[e]]=dist[x]+w[e];

ans=true;

if(!inq[v[e]])

{

inq[v[e]]=true;

q.push(v[e]);

}

}

}

return ans;

}

三、Floyd-Warshall算法:任意两点之间的最短路

算法要求:无特殊要求。

算法思想:动态规划。

时间复杂度:O(n^3)

算法代码:

#define MAXN 100

#define INF (1<<31)-1

int n,m,p,q;

int f[MAXN+10][MAXN+10];

void Floyd()

{

inti,j,k;

for(k=0;k<n;k++)

{

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

{

for(j=0;j<n;j++)

{

if(f[i][k]+f[k][j]<f[i][j])

f[i][j]=f[i][k]+f[k][j];

}

}

}

if(f[0][n-1]==INF) printf(“0\n”);

else printf(“%d\n”,f[0][n-1]);

}

int main()

{

inta,b,c,i;

while(~scanf(“%d%d”,&n,&m))

{

if(!n&&!m) break;

for(p=0;p<n;p++)

for(q=0;q<n;q++)

f[p][q]=INF;

for(i=0;i<m;i++)

{

scanf(“%d%d%d”,&a,&b,&c);

f[a][b]=c;

f[b][a]=c;

}

floyd();

}

return 0;

}

四、SPFA算法:单源点最短路的高效实用算法

算法要求:无特殊要求

算法思想:用FIFO队列优化的BF算法。

算法时间复杂度:O(k|E|),k为常数,一般k<=2

算法代码:

#include <queue>

#######省略部分######

#define INF (1<<31)-1

#define N 1010

int dist[N],n,m;

int edge[N][N];

bool vis[N];

void spfa(int s)

{

inti,u;

memset(vis,false,sizeof(vis));

for(i=0;i<n;i++)dist[i]=INF;

queue<int>q;

q.push(s);

vis[s]=true;

dist[s]=0;

while(!q.empty())

{

u=q.front();

q.pop();

vis[u]=false;

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

{

if(dist[i]>dist[u]+edge[u][i])

dist[i]=dist[u]+edge[u][i];

if(!vis[i])

{

vis[i]=true;

q.push(i);

}

}

}

}

int main()

{

inti,j,a,b,c,origin;

while(scanf(“%d%d”,&n,&m)!=EOF&&(n||m))

{

for(i=0;i<N;i++)

for(j=0;j<i;j++)

{

edge[i][j]=INF;

edge[j][i]=INF;

}

},

for(i=0;i<m;i++)

{

scanf(“%d%d%d”,&a,&b,&c);

if(edge[a][b]>c)

{

edge[a][b]=c;

edge[b][a]=c;

}

scanf(“%d”,&origin);

spfa(origin);

printf(“%d\n”,dist[n]);

}

return 0;

}

时间: 2024-10-31 06:20:54

最短路(转)模板的相关文章

基础最短路(模板 bellman_ford)

Description 在每年的校赛里,所有进入决赛的同学都会获得一件很漂亮的t-shirt.但是每当我们的工作人员把上百件的衣服从商店运回到赛场的时候,却是非常累的!所以现在他们想要寻找最短的从商店到赛场的路线,你可以帮助他们吗? Input 输入包括多组数据.每组数据第一行是两个整数N.M(N<=100,M<=10000),N表示成都的大街上有几个路口,标号为1的路口是商 店所在地,标号为N的路口是赛场所在地,M则表示在成都有几条路.N=M=0表示输入结束.接下来M行,每行包括3个整数A,

基础最短路(模板 spfa)

Description 虽然草儿是个路痴(就是在杭电待了一年多,居然还会在校园里迷路的人,汗~),但是草儿仍然很喜欢旅行,因为在旅途中 会遇见很多人(白马王子,^0^),很多事,还能丰富自己的阅历,还可以看美丽的风景……草儿想去很多地方,她想要去东京铁塔看夜景,去威尼斯看电影,去 阳明山上看海芋,去纽约纯粹看雪景,去巴黎喝咖啡写信,去北京探望孟姜女……眼看寒假就快到了,这么一大段时间,可不能浪费啊,一定要给自己好好的放个 假,可是也不能荒废了训练啊,所以草儿决定在要在最短的时间去一个自己想去的地

最短路算法模板合集(Dijkstar,Dijkstar(优先队列优化), 多源最短路Floyd)

再开始前我们先普及一下简单的图论知识 图的保存: 1.邻接矩阵. G[maxn][maxn]; 2.邻接表 邻接表我们有两种方式 (1)vector< Node > G[maxn]; 这个是之前就定义了图的大小了,再下面使用的时候就不用对图的大小进行申请了, 但是因为是直接申请了大小 要对图进行初始化,因此可能在某些题目中这样使用的话会超时 (2)vector< vector<Node> > G; 这个是未定义大小,但是在使用之前要对其的大小内存进行申请. G.resi

K短路【模板】

A*+SPFA算法: (1)将有向图的所有边正向.反向分别存入两个不同的边集(Edges,Edges1)中.用反向边集,以所求终点t为源点,利用SPFA或Dijkstra求解出所有点到t的最短路径,用Dist[i]数组来表示点i到点t的最短距离. (2)建立一个优先队列,将源点s加入到队列中. (3)从优先队列中取出最小的点p,如果点p == t,则计算t出队的次数.如果当前路径长度就是s到t的第k短路长度,算法结束.否则遍历与p相连的所有的边,将扩展出的到p的邻接点信息加入到优先队列中取. 注

HDU 2544最短路dijkstra模板题

最短路 Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 33657    Accepted Submission(s): 14617 Problem Description 在每年的校赛里,所有进入决赛的同学都会获得一件很漂亮的t-shirt.但是每当我们的工作人员把上百件的衣服从商店运回到赛场的时候,却是非常累的!所以现在他们想要寻找

hdu 2544 最短路 dijkstra模板

最短路 Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 38804    Accepted Submission(s): 16925 Problem Description 在每年的校赛里,所有进入决赛的同学都会获得一件很漂亮的t-shirt.但是每当我们的工作人员把上百件的衣服从商店运回到赛场的时候,却是非常累的!所以现在他们想要寻找最

最短路算法--模板

迪杰斯特拉算法(Dijkstra): Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径. 主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止. 1 #include <iostream> 2 using namespace std; 3 const int maxnum = 100; 4 const int maxint = 999999; 5 6 // 各数组都从下标1开始 7 int dist[maxnum]; // 表示当前点到源

HDU1584-A strange lift-最短路(Dijkstra模板题)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1548 这个题目很容易让人用广搜...无语... #include<iostream> #include<string> #include<cstdio> #include<cstring> #include<map> #include<queue> #include<cmath> #include<stack> #

最短路Dijkstra模板

传送门:单源最短路 1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 6 const int MAXN = 10000+1; 7 const int MAXM = 500000+1; 8 const int INT_MAX = 2147483647; 9 10 struct EDGE{ 11 int next,to,dis; 12 }Edge[MAX

蓝桥杯训练 最短路 (SPFA模板 vector)

算法训练 最短路 时间限制:1.0s   内存限制:256.0MB 问题描述 给定一个n个顶点,m条边的有向图(其中某些边权可能为负,但保证没有负环).请你计算从1号点到其他点的最短路(顶点从1到n编号). 输入格式 第一行两个整数n, m. 接下来的m行,每行有三个整数u, v, l,表示u到v有一条长度为l的边. 输出格式 共n-1行,第i行表示1号点到i+1号点的最短路. 样例输入 3 3 1 2 -1 2 3 -1 3 1 2 样例输出 -1 -2 数据规模与约定 对于10%的数据,n