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

Posted mered1th

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了图的算法专题——最短路径相关的知识,希望对你有一定的参考价值。

概要:

  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
",s);
35     }
36     DFS(s,pre[v]);
37     printf("%d
",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 }

 

 

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

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

 

 


 

 

 

 

 

以上是关于图的算法专题——最短路径的主要内容,如果未能解决你的问题,请参考以下文章

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

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

最短路径问题-Dijkstra(基于图的ADT)

算法90----图的最短路径

图的最短路径的Dijkstra算法及Floyd算法

图的应用——最短路径(迪杰斯特拉算法)