图的算法专题——最短路径

概要:

  1. Dijkstra算法
  2. Bellman-Ford算法
  3. SPFA算法
  4. Floyd算法


1、Dijkstra算法用于解决单源最短路径问题,严格讲是无负权图的最短路径问题。

邻接矩阵版

 1 const int maxv=1000;
 2 const int INF=1000000000;
 3 int n,G[maxv][maxv];
 4 int d[maxv];  //起点到各点的最短路径长度
 5 bool vis[maxv]={false};
 6
 7 void Dijkstra(int s){ //s为起点
 8     fill(d,d+maxv,INF);
 9     d[s]=0;
10     for(int i=0;i<n;i++){ //循环n次
11         int u=-1,MIN=INF;   //u使d[u]最小,MIN存放最小d[u]
12         for(int j=0;j<n;j++){
13             if(vis[j]==false && d[j]<MIN){ //未收录的顶点中到起点距离最小者
14                 u=j;
15                 MIN=d[j];
16             }
17         }
18         //找不到小于INF的d[u],说明剩下的顶点和起点s不连通
19         if(u==-1) return;
20         vis[u] =true;
21         for(int v=0;v<n;v++){
22             if( G[u][v]!=INF && vis[v]==false && d[u]+G[u][v] < d[v]){
23                 d[v]=d[u]+G[u][v];
24             }
25         }
26     }
27 }

邻接表版

 1 struct Node{
 2     int v,dis; //v为边的目标顶点, dis为边权
 3 };
 4 vector<Node> Adj[maxv];
 5 int n,d[maxv];
 6 bool vis[maxv]={0};
 7
 8 void Dijkstra(int s){
 9     fill(d,d+maxv,INF);
10     d[s]=0;
11     for(int i=0;i<n;i++) {
12         int u=-1,MIN=INF;
13         for(int j=0;j<n;j++){
14             if(d[j]<MIN && vis[j]==false){
15                 u=j;
16                 MIN=d[j];
17             }
18         }
19         if(u==-1) return;
20         vis[u]=true;
21         for(int j=0;j<Adj[u].size();j++){
22             int v=Adj[u][j].v;
23             if(vis[v]==false && d[u] +Adj[u][j].dis <d[v]){
24                 d[v]=d[u]+Adj[u][j].dis;
25             }
26         }
27     }
28 }

若要求输出最短路径,以邻接矩阵为例:

 1 const int maxv=1000;
 2 const int INF=1000000000;
 3 int n,G[maxv][maxv];
 4 int d[maxv];  //起点到各点的最短路径长度
 5 bool vis[maxv]={false};
 6 int pre[maxv];
 7
 8 void Dijkstra(int s){ //s为起点
 9     fill(d,d+maxv,INF);
10     for(int i=0;i<n;i++) pre[i]=i;
11     d[s]=0;
12     for(int i=0;i<n;i++){ //循环n次
13         int u=-1,MIN=INF;   //u使d[u]最小,MIN存放最小d[u]
14         for(int j=0;j<n;j++){
15             if(vis[j]==false && d[j]<MIN){ //未收录的顶点中到起点距离最小者
16                 u=j;
17                 MIN=d[j];
18             }
19         }
20         //找不到小于INF的d[u],说明剩下的顶点和起点s不连通
21         if(u==-1) return;
22         vis[u] =true;
23         for(int v=0;v<n;v++){
24             if( G[u][v]!=INF && vis[v]==false && d[u]+G[u][v] < d[v]){
25                 d[v]=d[u]+G[u][v];
26                 pre[v]=u;
27             }
28         }
29     }
30 }
31
32 void DFS(int s,int v){  //从终点开始递归
33     if(v==s){ //如果当前已经到达起点,输出起点并返回
34         printf("%d\n",s);
35     }
36     DFS(s,pre[v]);
37     printf("%d\n",v);
38 }

另外还有一种情况,如果某个结点存在多个前驱结点,那上面这种pre数组的方法就不再适用,改成vector即可:

 1 const int maxv=1010;
 2 const int INF=1000000000;
 3 vector<int> pre[maxv];
 4 void Dijkstra(int s){
 5     fill(d,d+maxv,INF);
 6     d[s]=0;
 7     for(int i=0;i<n;i++){
 8         int u=-1,MIN=INF;
 9         for(int j=0;j<n;j++){
10             if(vis[j]==false && d[j]<MIN){
11                 u=j;
12                 MIN=d[j];
13             }
14         }
15         if(u==-1) return;
16         vis[u]=true;
17         for(int v=0;v<n;v++){
18             if(vis[v]==false &&G[u][v]!=INF){
19                 if(d[u]+G[u][v]<d[v]){
20                     d[v]=d[u]+G[u][v];
21                     pre[v].clear();
22                     pre[v].push_back(u);
23                 }
24                 else if(d[u]+G[u][v]==d[v]){
25                     pre[v].push_back(u);
26                 }
27             }
28         }
29     }
30 }

当访问的结点是路径起点st时(边界),此时tempPath里存了整条路径(倒序),这时需要计算第二标尺value的值,并与optValue比较,若更优则更新optValue并把path覆盖。

 1 const int maxv=1010;
 2 const int INF=1000000000;
 3 int optValue;
 4 vector<int> path,tempPath;
 5 vector<int> pre[maxv];
 6
 7 void Dijkstra(int s){
 8     fill(d,d+maxv,INF);
 9     d[s]=0;
10     for(int i=0;i<n;i++){
11         int u=-1,MIN=INF;
12         for(int j=0;j<n;j++){
13             if(vis[j]==false && d[j]<MIN){
14                 u=j;
15                 MIN=d[j];
16             }
17         }
18         if(u==-1) return;
19         vis[u]=true;
20         for(int v=0;v<n;v++){
21             if(vis[v]==false &&G[u][v]!=INF){
22                 if(d[u]+G[u][v]<d[v]){
23                     d[v]=d[u]+G[u][v];
24                     pre[v].clear();
25                     pre[v].push_back(u);
26                 }
27                 else if(d[u]+G[u][v]==d[v]){
28                     pre[v].push_back(u);
29                 }
30             }
31         }
32     }
33 }
34
35 void DFS(int v){ //v为当前访问结点
36     if(v==st){
37         tempPath.push_back(v);
38         int value;
39         (计算路径的value)
40         if(value优于optValue){
41             path=tempPath;
42             optValue=value;
43         }
44         tempPath.pop_back(); //将刚加入的结点删除
45         return;
46     }
47     tempPath.push_back(v);
48     for(int i=0;i<pre[v].size();i++){
49         DFS(pre[v][i]);
50     }
51     tempPath.pop_back();
52 }

除此之外,还会碰到第二标尺,常见有以下三种:(具体代码见晴神算法笔记,写的很清楚)

  • 新增边权(如增加开销)
  • 新增点权(如收集到的物资)
  • 求最短路径条数


图的算法专题——最短路径

原文地址:https://www.cnblogs.com/Mered1th/p/10418946.html

时间: 2024-10-06 18:43:24

图的算法专题——最短路径的相关文章

算法系列笔记8(有关图的算法二—最短路径问题)

图的最短路径问题主要分为两类,单源最短路径问题和全对最短路径问题.单源最短路径问题指给点单个源点,求其到所有其它顶点之间的最短距离.而全对最短路径问题指所有顶点之间的最短路劲问题.此外对于单对最短路径问题,从渐进意义上来看,目前还没有比最好的单元算法更快的算法来解决这一问题. 一:单源最短路径问题 单源最短路劲问题根据其权重分为四类,当图G=(V,E)为无权图,直接使用广度优先遍历(这里不做介绍):当权值为非负值,则使用Dijkstra算法:存在负权值及负权环,可以使用Bellman-Ford算

算法导论——所有点对最短路径:稀疏图Johnson算法

package org.loda.graph; import org.loda.structure.Stack; import org.loda.util.In; /** * * @ClassName: Johnson 时间复杂度:EVlgV * @Description: 稀疏图上的johnson算法,由于稀疏图的数据结构推荐使用邻接链表,所以这里也采用邻接链表,该算法也是给稀疏图使用的,如果是密集图,推荐使用实现较为简单的FloydWashall算法,可以保证V^3的时间复杂度 * * Jo

图论(二):图的四种最短路径算法

本文总结了图的几种最短路径算法的实现:深度或广度优先搜索算法,弗洛伊德算法,迪杰斯特拉算法,Bellman-Ford算法 1),深度或广度优先搜索算法(解决单源最短路径)从起始结点开始访问所有的深度遍历路径或广度优先路径,则到达终点结点的路径有多条,取其中路径权值最短的一条则为最短路径. 下面是核心代码: void dfs(int cur, int dst){ /***operation***/ /***operation***/ if(minPath < dst) return;//当前走过路

带负权图的单源最短路径算法:Bellman-Ford算法

算法简介 前面介绍过图的单源最短路径算法Dijkstra算法,然而Dijkstra算法无法判断含负权边的图的最短路.如果遇到负权,在没有负权回路存在时(负权回路的含义是,回路的权值和为负.)即便有负权的边,也可以采用Bellman-Ford算法正确求出最短路径. Bellman-Ford算法能在更普遍的情况下(存在负权边)解决单源点最短路径问题.对于给定的带权(有向或无向)图G=(V,E),其源点为s,加权函数 w是 边集 E 的映射.对图G运行Bellman-Ford算法的结果是一个布尔值,表

算法导论——lec 13 贪心算法与图上算法

之前我们介绍了用动态规划的方法来解决一些最优化的问题.但对于有些最优化问题来说,用动态规划就是"高射炮打蚊子",采用一些更加简单有效的方法就可以解决.贪心算法就是其中之一.贪心算法是使所做的选择看起来是当前最佳的,期望通过所做的局部最优选择来产生一个全局最优解. 一. 活动选择问题 [问题]对几个互相竞争的活动进行调度:活动集合S = {a1, a2, ..., an},它们都要求以独占的方式使用某一公共资源(如教室),每个活动ai有一个开始时间si和结束时间fi ,且0 ≤ si &

图的算法

因为图算法的各种英文名傻傻分不清楚,所以总结一下. 求最小生成树: Kruskal 假设给定一个加权连通图G,G的边集合为E,顶点个数为n,要求其一棵最小生成树T. 假设T中的边和顶点均涂成红色,其余边为白色.开始时G中的边均为白色. 1)将所有顶点涂成红色: 2)在白色边中,挑选一条权最小的边,使其与红色边不形成圈,将该白色边涂红: 3)重复2)直到有n-1条红色边,这n-1条红色边便构成最小生成树T的边集合. prime算法 1.清空生成树,任取一个顶点加入生成树 2.在那些一个端点在生成树

图论算法之最短路径

图论算法之最短路径 作者:jasonkent27 转载请注明出处:www.cnblogs.com/jasonkent27 1. 前言 1.1 最短路引入 小明和小天现在住在海口(C1),他们俩计划暑假到三亚(C4)玩一趟,在海口和三亚之间有许多中间城市(文昌,临高,乐东,万宁...)图中的边上的数字是他们到达该城市必须的花费,现在需要你帮他们俩找出一条从海口到三亚的最省钱的路径出来. 等等,图中的边的weight怎么会有负的呢?你暂且可以这么理解吧.图中的边上的weight可以当作他们旅途中必须

算法_最短路径

一.概述  定义:在一幅加权有向图中,从顶点s到顶点t的最短路径是所有从s到t的路径中的权重的最小者.从定义可以看出单点最短路径的实现是建立在加权有向图的基础上的. 最短路径树:给定一幅加权有向图和一个顶点s,以s为起点的一颗最短路径树是图的一幅子图,它包含s和从s可达的所有顶点.这颗有向树的根节点是s,树的每条路径都是有向图中的一条最短路径.它包含了顶点s到所有可达的顶点的最短路径. 二.加权有向图和加权有向边的数据结构 加权有向图和加权有向边的数据结构和加权无向图无向边的数据结构类型基本相同

算法导论——最短路径:BellmanFord算法

package org.loda.graph; import org.loda.structure.Stack; import org.loda.util.In; /** * * @ClassName: BellmanFord * @Description: 最短路径问题 * * 通用最短路径算法,能解决除了含负权重环以外的所有情况的最短路径,包括了含有负权重边的有向加权图 * * @author minjun * @date 2015年5月28日 下午11:04:45 * */ public