圖的算法專題——最短路徑
阿新 • • 發佈:2019-02-23
說明 turn () path 刪除 鄰接矩陣 else bool 短路徑
概要:
- Dijkstra算法
- Bellman-Ford算法
- SPFA算法
- 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 }
除此之外,還會碰到第二標尺,常見有以下三種:(具體代碼見晴神算法筆記,寫的很清楚)
- 新增邊權(如增加開銷)
- 新增點權(如收集到的物資)
- 求最短路徑條數
圖的算法專題——最短路徑