最短路的四種算法

這裏總結複習一下最短路的四種算法php

------------------------------------------------html

如下題目過於佔用篇幅,請自行上OJ看題node

1)Floyd算法ios

  Floyd算法又稱爲插點法,是一種利用動態規劃的思想尋找給定的加權圖中多源點之間最短路徑的算法,與Dijkstra算法相似。(百度百科)算法

  重點是多源點。數組

  鄰接矩陣 mp[i][j] 表示的是從 i 點到 j 點的權值,如今在 i 到 j 之間插一個 k 點,那麼狀態轉移方程爲 mp[i][j] = min(mp[i][k] + mp[k][j], mp[i][j]);ide

  這算法缺點是時間複雜度大,好像一個圖到 500 個點就會爆時間了。。。。測試

  核心代碼優化

 

1 for(int k = 1;k <= n;++k)
2     for(int i = 1;i <= n;++i)
3         for(int j = 1;j <= n;++j)
4         mp[i][j] = min(mp[i][k] + mp[k][j], mp[i][j]);

 

  HDU1874 暢通工程續(這題有幾個坑點注意一下就好)spa

這裏用Floyd算法AC

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <string>
 4 #include <cstring>
 5 #include <cmath>
 6 #include <sstream>
 7 #include <algorithm>
 8 #include <set>
 9 #include <map>
10 #include <vector>
11 #include <queue>
12 #include <iomanip>
13 #include <stack>
14 
15 using namespace std;
16 
17 typedef long long LL;
18 const int INF = 0x3f3f3f3f;
19 const int MAXN = 100005;
20 const int MOD = 1e9 + 7;
21 
22 #define MemN(x) memset(x, -1, sizeof(x))
23 #define Mem0(x) memset(x, 0, sizeof(x))
24 #define MemM(x) memset(x, 0x3f, sizeof(x))
25 
26 int main()
27 {
28     int n, m, dp[205][205];
29     int a, b, c;
30     while(cin >> n >> m)
31     {
32         MemM(dp);
33         int i, j, k;
34         for(i = 0;i < n;++i)
35             dp[i][i] = 0;
36         for(i = 0;i < m;++i)
37         {
38             cin >> a >> b >> c;
39             dp[a][b] = min(dp[a][b], c);
40             dp[b][a] = dp[a][b];
41         }
42 
43         for(k = 0;k < n;++k)
44             for(i = 0;i < n;++i)
45                 for(j = 0;j < n;++j)
46                 dp[i][j] = min(dp[i][j], dp[i][k] + dp[k][j]);
47         cin >> a >> b;
48         if(dp[a][b] != INF)
49             cout << dp[a][b] << endl;
50         else
51             cout << -1 << endl;
52     }
53 }
View Code

2)Dijkstra算法(沒錯,寫 Dijkstra 比寫 迪傑斯特拉 看起來更厲害的樣子)

  這裏先介紹基礎的 Dijkstra 算法,文章後面再說 Dijkstra 算法的優化。注意 Dijkstra 算法只適用無負權值的單源點最短路。這個算法跟最小生成樹的 Prim算法 相似。

  Dijkstra 算法的核心思想是:鬆弛。例若有 a, b, c , d 三個點,有三條邊 a -> b = 1, a -> c = 5, b ->c = 2, c -> d = 1(我不會畫畫,就文字說明了)

從 a 開始,因此 dis[b] = 1, dis[c] = 5,dis[d] = ∞

而後選出最短的邊 mi = 1,對應點爲 b,就以 b 爲中點鬆弛,即從 a -> c 跟 a ->b -> c比較

dis[c] = min(dis[c], mi + mp[b][c]),下面 a -> d 相似

dis[d] = min(dis[d], mi + mp[b][d]) 這樣更新 dis,具體看代碼吧

  核心代碼

 1 int dis[MAXN], n, mp[MAXN][MAXN];
 2 bool vis[MAXN];
 3 //從 s 點開始的單源最短路
 4 void Dijkstra(int s)
 5 {
 6     memset(vis, 0, sizeof(vis));
 7     int i, j;
 8     for(i = 1;i <= n;++i)
 9         dis[i] = mp[s][i];
10     vis[s] = 1;
11     int v, mi;
12     //每次都 vis 一個點,加上上面一次 vis, 因此一共 vis n次
13     for(i = 1;i < n;++i)
14     {
15         //選出當前離 s 最近的點
16         mi = INF;
17         for(j = 1;j <= n;++j)
18         {
19             if(!vis[j] && dis[j] < mi)
20             {
21                 mi = dis[j];
22                 v = j;
23             }
24         }
25         vis[v] = 1;
26         //更新 dis
27         for(j = 1;j <= n;++j)
28             if(!vis[j] && mi + mp[v][j] < dis[j])
29             dis[j] = mi + mp[v][j];
30     }
31 }

上面那道杭電也能夠用Dijkstra 算法作作看,這裏給出另外一道題

POJ-2387  Til the Cows Come Home

 Dijkstra算法 AC

 

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <string>
 4 #include <cstring>
 5 #include <cmath>
 6 #include <sstream>
 7 #include <algorithm>
 8 #include <set>
 9 #include <map>
10 #include <vector>
11 #include <queue>
12 #include <iomanip>
13 #include <stack>
14 
15 using namespace std;
16 
17 typedef long long LL;
18 //我也不知道爲何這裏改爲這個就能過,以前那個 0x3f3f3f3f 不會爆 int 啊,坑我 WA 到絕望
19 const int INF = 999999999;
20 const int MAXN = 1005;
21 const int MOD = 1e9 + 7;
22 
23 #define MemI(x) memset(x, -1, sizeof(x))
24 #define Mem0(x) memset(x, 0, sizeof(x))
25 #define MemM(x) memset(x, 0x3f, sizeof(x))
26 
27 int dis[MAXN], n, mp[MAXN][MAXN];
28 bool vis[MAXN];
29 //從 s 點開始的單源最短路
30 void Dijkstra(int s)
31 {
32     memset(vis, 0, sizeof(vis));
33     int i, j;
34     for(i = 1;i <= n;++i)
35         dis[i] = mp[s][i];
36     vis[s] = 1;
37     int v, mi;
38     //每次都 vis 一個點,加上上面一次 vis, 因此一共 vis n次
39     for(i = 1;i < n;++i)
40     {
41         //選出當前離 s 最近的點
42         mi = INF;
43         for(j = 1;j <= n;++j)
44         {
45             if(!vis[j] && dis[j] < mi)
46             {
47                 mi = dis[j];
48                 v = j;
49             }
50         }
51         vis[v] = 1;
52         //更新 dis
53         for(j = 1;j <= n;++j)
54             if(!vis[j] && (mi + mp[v][j]) < dis[j])
55             dis[j] = mi + mp[v][j];
56     }
57 }
58 
59 int main()
60 {
61     int T;
62     while(scanf("%d%d", &T, &n) != EOF)
63     {
64         for(int i = 1;i <= n;++i)
65         {
66             for(int j = 1;j <= n;++j)
67             {
68                 if(i != j)
69                     mp[i][j] = INF;
70                 else
71                     mp[i][j] = 0;
72             }
73         }
74         int a, b, c;
75         while(T--)
76         {
77             scanf("%d%d%d", &a, &b, &c);
78             if(c < mp[a][b])
79                 mp[a][b] = mp[b][a] = c;
80         }
81         Dijkstra(n);
82         printf("%d\n", dis[1]);
83     }
84     return 0;
85 }
View Code

 

另:這裏給出另外一道題POJ-2253 Frogger

雖然不是求最短路,可是能夠用 Dijkstra算法,至於爲何能用它?

考慮目前最短路的兩種算法:Floyd 算法,和 Dijkstra 算法,前者是能夠說是直接求a 到 b 的距離,並無涉及到通過過程,可是後者涉及到通過過程,因此有時候求路徑過程的最大值或最小值能夠考慮 Dijkstra算法,下面Dijkstra算法AC代碼

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <string>
 4 #include <cstring>
 5 #include <cmath>
 6 #include <sstream>
 7 #include <algorithm>
 8 #include <set>
 9 #include <map>
10 #include <vector>
11 #include <queue>
12 #include <iomanip>
13 #include <stack>
14 
15 using namespace std;
16 
17 typedef long long LL;
18 const int INF = 0x3f3f3f3f;
19 const int MAXN = 205;
20 const int MOD = 1e9 + 7;
21 
22 #define MemI(x) memset(x, -1, sizeof(x))
23 #define Mem0(x) memset(x, 0, sizeof(x))
24 #define MemM(x) memset(x, 0x3f, sizeof(x))
25 
26 struct Node
27 {
28     int x, y;
29 }node[MAXN];
30 double mp[MAXN][MAXN], dis[MAXN];
31 int n;
32 bool vis[MAXN];
33 
34 double Cal(int x1, int y1, int x2, int y2)
35 {
36     return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
37 }
38 
39 void Dijkstra()
40 {
41     Mem0(vis);
42     double mi;
43     int i, j, v;
44     for(i = 1;i <= n;++i)
45         dis[i] = mp[1][i];
46     vis[1] = true;
47     for(i = 1;i < n;++i)
48     {
49         mi = INF;
50         for(j = 1;j <= n;++j)
51             if(!vis[j] && dis[j] < mi)
52         {
53             mi = dis[j];
54             v = j;
55         }
56         vis[v] = true;
57         //dis 表示該路徑上的最大的一段
58         for(j = 1;j <= n;++j)
59             if(!vis[j] && mp[v][j] < dis[j])
60             dis[j] = max(mi, mp[v][j]);
61 
62     }
63 }
64 
65 int main()
66 {
67     int cnt = 1;
68     while(scanf("%d", &n) != EOF && n)
69     {
70         int i, j, k;
71         for(i = 1;i <= n;++i)
72             scanf("%d%d", &node[i].x, &node[i].y);
73         //存點與點的距離
74         for(i = 1;i <= n;++i)
75             for(j = 1;j <= n;++j)
76             mp[i][j] = Cal(node[i].x, node[i].y, node[j].x, node[j].y);
77 
78         Dijkstra();
79         if(cnt != 1)
80             printf("\n");
81         printf("Scenario #%d\nFrog Distance = %.3f\n", cnt++, dis[2]);
82     }
83     return 0;
84 }
View Code

Floyd算法也算 dp 的一種,因此更改一下 dp 條件也是能夠作的(手動滑稽),下面Floyd算法AC,根據OJ反應,這裏Floyd算法的時間是Dijkstra算法的6倍

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <string>
 4 #include <cstring>
 5 #include <cmath>
 6 #include <sstream>
 7 #include <algorithm>
 8 #include <set>
 9 #include <map>
10 #include <vector>
11 #include <queue>
12 #include <iomanip>
13 #include <stack>
14 
15 using namespace std;
16 
17 typedef long long LL;
18 const int INF = 0x3f3f3f3f;
19 const int MAXN = 205;
20 const int MOD = 1e9 + 7;
21 
22 #define MemI(x) memset(x, -1, sizeof(x))
23 #define Mem0(x) memset(x, 0, sizeof(x))
24 #define MemM(x) memset(x, 0x3f, sizeof(x))
25 
26 struct Node
27 {
28     int x, y;
29 }node[MAXN];
30 double mp[MAXN][MAXN], dis[MAXN];
31 int n;
32 bool vis[MAXN];
33 
34 double Cal(int x1, int y1, int x2, int y2)
35 {
36     return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
37 }
38 
39 void Dijkstra()
40 {
41     Mem0(vis);
42     double mi;
43     int i, j, v;
44     for(i = 1;i <= n;++i)
45         dis[i] = mp[1][i];
46     vis[1] = true;
47     for(i = 1;i < n;++i)
48     {
49         mi = INF;
50         for(j = 1;j <= n;++j)
51             if(!vis[j] && dis[j] < mi)
52         {
53             mi = dis[j];
54             v = j;
55         }
56         vis[v] = true;
57         //dis 表示該路徑上的最大的一段
58         for(j = 1;j <= n;++j)
59             if(!vis[j] && mp[v][j] < dis[j])
60             dis[j] = max(mi, mp[v][j]);
61 
62     }
63     printf("%.3f\n", dis[2]);
64 }
65 
66 void Floyd()
67 {
68     for(int k = 1;k <= n;++k)
69         for(int i = 1;i <= n;++i)
70             for(int j = 1;j <= n;++j)
71             if(mp[i][k] < mp[i][j] && mp[k][j] < mp[i][j])
72             mp[i][j] = max(mp[i][k], mp[k][j]);
73     printf("%.3f\n", mp[1][2]);
74 }
75 
76 int main()
77 {
78     int cnt = 1;
79     while(scanf("%d", &n) != EOF && n)
80     {
81         int i, j, k;
82         for(i = 1;i <= n;++i)
83             scanf("%d%d", &node[i].x, &node[i].y);
84         //存點與點的距離
85         for(i = 1;i <= n;++i)
86             for(j = 1;j <= n;++j)
87             mp[i][j] = Cal(node[i].x, node[i].y, node[j].x, node[j].y);
88         if(cnt != 1)
89             printf("\n");
90         printf("Scenario #%d\nFrog Distance = ", cnt++);
91 //        Dijkstra();
92         Floyd();
93     }
94     return 0;
95 }
View Code

 

3)Bellman-Ford 算法

這篇博客比較清楚,能夠去看一下

https://www.cnblogs.com/godfray/p/4077146.html

Bellman - ford算法是求含負權圖的單源最短路徑的一種算法,Dijkstra算法沒法判斷含負權邊的圖的最短路。Bellman-Ford算法能在更廣泛的狀況下(存在負權邊)解決單源點最短路問題。對圖G運行Bellman - Ford算法的結果是一個布爾值,代表圖中是否存在着一個從源點s可達的負權迴路。(百度百科)

步驟:
一、初始化:跟 Dijkstra 同樣開一個同樣意義的 dis 數組,設源點爲 s ,則 dis[s] = 0,其餘 dis[] = ∞
二、鬆弛操做:對全部的邊進行鬆弛操做,此過程重複 n - 1 次,(n 爲點的個數)
三、檢驗負權迴路:遍歷全部的邊,若存在 d(v) > d(u) + w(u, v)(從 u 到 v 的邊)則返回false。
其實這個算法像是 bfs,我就下面盜來的圖(上面博客的)來看着理解吧。

給模板啦

 1 //從 u 到 v 的權值爲 w
 2 struct Edge
 3 {
 4     int u, v, w;
 5 }edge[MAXN];
 6 //n 個點 m 條邊
 7 int n, m, dis[MAXN];
 8 
 9 bool Bellman_Ford(int s)
10 {
11     int i, j;
12     //初始化,如上圖
13     for(i = 1;i <= n;++i)
14         dis[i] = INF;
15     dis[s] = 0;
16     //而後像圖那樣 bfs,最壞狀況是每次 bfs 都只遍歷到一個點,
17     //因此除源點外最多遍歷 n - 1 次,就獲得到該點的最短路
18     //關於轉移方程,能夠理解成 s -> u -> v 跟 s -> v 比較
19     for(i = 1;i < n;++i)
20         for(j = 0;j < m;++j)
21             if(dis[edge[j].v] > dis[edge[j].u] + edge[j].w)
22             dis[edge[j].v] = dis[edge[j].u] + edge[j].w;
23     //上面的過程已經將全部點都變成最短路了,負權迴路除外
24     //由於若是在負權迴路中無限循環,那麼最短路將是無窮小
25     //即若是還存在能夠鬆弛的邊,即存在負權迴路
26     for(i = 0;i < m;++i)
27         if(dis[edge[i].v] > dis[edge[j].u] + edge[j].w)
28         return false;
29     return true;
30 }

這個算法看看就好,時間複雜度過高了,下面是他的隊列優化版,用下面的。

4)SPAF算法

仍是別人家的博客好啊

https://www.cnblogs.com/jiangu66/p/3235361.html

https://blog.csdn.net/xunalove/article/details/70045815

其實 SPAF 算法是 Belleman-Ford算法的隊列優化而已

 就跟Belleman-Ford同樣,用一個 dis 數組,而後將能鬆馳的邊對應的終點加入隊列,而後就出隊入隊了。我仍是盜上博客二的吧

spfa的算法思想(動態逼近法):
    設立一個先進先出的隊列q用來保存待優化的結點,優化時每次取出隊首結點u,而且用u點當前的最短路徑估計值對離開u點所指向的結點v進行鬆弛操做,若是v點的最短路徑估計值有所調整,且v點不在當前的隊列中,就將v點放入隊尾。這樣不斷從隊列中取出結點來進行鬆弛操做,直至隊列空爲止。

不過看不少人的模板使用數組模擬隊列。。。。這就要注意數組的大小,聽說在稀疏圖中平均每一個點進隊不超過兩次,可能開 2 * n 就夠了吧??

SPAF算法判斷負權迴路:某點進隊超過 n - 1 次!!!!

這個仍是博客二的算法描述,當僞代碼看(沒有判斷負權迴路!!!!)

 1 void  spfa(s);  //求單源點s到其它各頂點的最短距離
 2     for i=1 to n do { dis[i]=∞; vis[i]=false; }   //初始化每點到s的距離,不在隊列
 3     dis[s]=0;  //將dis[源點]設爲0
 4     vis[s]=true; //源點s入隊列
 5     head=0; tail=1; q[tail]=s; //源點s入隊, 頭尾指針賦初值
 6     while head<tail do {
 7        head+1;  //隊首出隊
 8        v=q[head];  //隊首結點v
 9        vis[v]=false;  //釋放對v的標記,能夠從新入隊
10        for 每條邊(v,i)  //對於與隊首v相連的每一條邊
11         if (dis[i]>dis[v]+a[v][i]){  //若是不知足三角形性質
12          dis[i] = dis[v] + a[v][i]   //鬆弛dis[i]
13         if (vis[i]=false) {tail+1; q[tail]=i; vis[i]=true;}} //不在隊列,則加入隊列(好像這裏原博主少了大括號,如今以修正)
14     } 

在實際應用的時候我習慣用鏈式前向星,下面關於鏈式前向星的構造,這東西我也是手動模擬數據才弄懂的,跟鄰接表相似。

就用上面那個圖作個鏈式前向星的測試操做吧,下面個人 head 組都初始化爲 -1 , 我看不少人初始化爲 0,看博的人注意點,別亂了。

 1 struct Edge
 2 {
 3     int to, w, next;
 4 }edge[MAXN];
 5 int head[MAXN], cnt;
 6 
 7 void Add(int u, int v, int w)
 8 {
 9     edge[++cnt].to = v;
10     edge[cnt].w = w;
11     edge[cnt].next = head[u];
12     head[u] = cnt;
13 }
14 
15 void Search(int s)
16 {
17     cout << s;
18     for(int i = head[s];i != -1;i = edge[i].next)
19         cout << " -> " << edge[i].to;
20     cout << endl;
21 }
22 
23 int main()
24 {
25     MemI(head);
26     int n, m;
27     cin >> n >> m;
28     int a, b, c;
29     for(int i = 0;i < m;++i)
30     {
31         cin >> a >> b >> c;
32         Add(a, b, c);
33     }
34     for(int i = 1;i <= n;++i)
35         Search(i);
36     return 0;
37 }
38 /*
39 5 7
40 1 2 2
41 1 5 10
42 2 5 7
43 2 3 3
44 5 3 6
45 4 5 5
46 3 4 4
47 */

 

給個SPAF算法的C++模板吧

 

struct Edge
{
    int to, w, next;
}edge[MAXN];
int head[MAXN], cnt;

int n, dis[MAXN];
bool vis[MAXN];
queue<int> q;
/*int book[MAXN];判斷負權迴路用, 板子沒有就註釋掉了*/

bool SPFA(int s)
{
    memset(vis, 0, sizeof(vis));
//    memset(book, 0, sizeof(book));
    memset(dis, 0x3f, sizeof(dis));
    while(!q.empty())
        q.pop();
    dis[s] = 0;
    vis[s] = true;
//    book[s]++;
    q.push(s);
    int v, i;
    while(!q.empty())
    {
        v = q.front();
        q.pop();
        vis[v] = false;
        for(i = head[v];i != -1;i = edge[i].next)
        {
            if(dis[edge[i].to] > dis[v] + edge[i].w)
            {
                dis[edge[i].to] = dis[v] + edge[i].w;
                if(!vis[edge[i].to])
                {
//                    if(book[edge[i].to] >= n - 1)
//                        return false;
//                    book[edge[i].to]++;
                    q.push(edge[i].to);
                }
            }
        }
    }
    return true;
}

 

沒找到合適的題啊!!!!隨便找道水題吧。。。。模板題

HDU 2544 最短路

http://acm.hdu.edu.cn/showproblem.php?pid=2544

這裏試試Bellman-Frod的模板過的

 

#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <cmath>
#include <sstream>
#include <algorithm>
#include <set>
#include <map>
#include <vector>
#include <queue>
#include <iomanip>
#include <stack>

using namespace std;

typedef long long LL;
const int INF = 0x3f3f3f3f;
const int MAXN = 10005;
const int MOD = 1e9 + 7;

#define MemI(x) memset(x, -1, sizeof(x))
#define Mem0(x) memset(x, 0, sizeof(x))
#define MemM(x) memset(x, 0x3f, sizeof(x))

struct Edge
{
    int u, v, w;
}edge[MAXN << 1];
int n, m, cnt, dis[105];

void Add(int a, int b, int c)
{
    edge[++cnt].u = a;
    edge[cnt].v = b;
    edge[cnt].w = c;
}

bool BellmanFord(int s)
{
    MemM(dis);
    dis[s] = 0;
    int i, j;
    for(i = 1;i < n;++i)
        for(j = 1;j <= m << 1;++j)
            if(dis[edge[j].v] > dis[edge[j].u] + edge[j].w)
            dis[edge[j].v] = dis[edge[j].u] + edge[j].w;
    for(j = 1;j <= m << 1;++j)
        if(dis[edge[j].v] > dis[edge[j].u] + edge[j].w)
        return false;
    return true;
}

int main()
{
    while(cin >> n >> m)
    {
        cnt = 0;
        if(!n && !m)
            break;
        int a, b, c;
        for(int i = 0;i < m;++i)
        {
            cin >> a >> b >> c;
            Add(a, b, c);
            Add(b, a, c);
        }
        BellmanFord(1);
        cout << dis[n] << endl;
    }
    return 0;
}
View Code

 

 

 

這裏用SPAF 過的

#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <cmath>
#include <sstream>
#include <algorithm>
#include <set>
#include <map>
#include <vector>
#include <queue>
#include <iomanip>
#include <stack>

using namespace std;

typedef long long LL;
const int INF = 0x3f3f3f3f;
const int MAXN = 10005;
const int MOD = 1e9 + 7;

#define MemI(x) memset(x, -1, sizeof(x))
#define Mem0(x) memset(x, 0, sizeof(x))
#define MemM(x) memset(x, 0x3f, sizeof(x))

struct Edge
{
    int to, w, next;
}edge[MAXN << 1];
int head[MAXN << 1], cnt;

int n, m, dis[MAXN];
bool vis[MAXN];
queue<int> q;
/*int book[MAXN];判斷負權迴路用, 板子沒有就註釋掉了*/

void Add(int a, int b, int c)
{
    edge[++cnt].to = b;
    edge[cnt].w = c;
    edge[cnt].next = head[a];
    head[a] = cnt;
}

bool SPFA(int s)
{
    memset(vis, 0, sizeof(vis));
//    memset(book, 0, sizeof(book));
    memset(dis, 0x3f, sizeof(dis));
    while(!q.empty())
        q.pop();
    dis[s] = 0;
    vis[s] = true;
//    book[s]++;
    q.push(s);
    int v, i;
    while(!q.empty())
    {
        v = q.front();
        q.pop();
        vis[v] = false;
        for(i = head[v];i != -1;i = edge[i].next)
        {
            if(dis[edge[i].to] > dis[v] + edge[i].w)
            {
                dis[edge[i].to] = dis[v] + edge[i].w;
                if(!vis[edge[i].to])
                {
//                    if(book[edge[i].to] >= n - 1)
//                        return false;
//                    book[edge[i].to]++;
                    q.push(edge[i].to);
                }
            }
        }
    }
    return true;
}

int main()
{
    while(cin >> n >> m)
    {
        cnt = 0;
        MemI(head);
        Mem0(edge);
        if(!n && !m)
            break;
        int a, b, c;
        while(m--)
        {
            cin >> a >> b >> c;
            Add(a, b, c);
            Add(b, a, c);
        }
        SPFA(1);
        cout << dis[n] << endl;
    }
    return 0;
}
View Code

 

下面說優化方面的。。。。。

5)Dijkstra算法堆優化

  若是圖爲稀疏圖的話,即邊數遠小於 n^2,能夠考慮用堆優化。主要是在取最小的邊那個地方優化,我的習慣採用鏈式前向星存圖,優先隊列來模擬最小堆。

https://www.cnblogs.com/dancer16/p/6964454.html

 用優先隊列,直接上模板題題解

 

#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <cmath>
#include <sstream>
#include <algorithm>
#include <set>
#include <map>
#include <vector>
#include <queue>
#include <iomanip>
#include <stack>

using namespace std;

typedef long long LL;
const int INF = 0x3f3f3f3f;
const int MAXN = 10005;
const int MOD = 1e9 + 7;

#define MemI(x) memset(x, -1, sizeof(x))
#define Mem0(x) memset(x, 0, sizeof(x))
#define MemM(x) memset(x, 0x3f, sizeof(x))

struct Edge
{
    int to, w, next;
}edge[MAXN << 1];
int head[MAXN], cnt;

int dis[MAXN], n, m;
//出隊是最小的
struct cmp
{
    bool operator () (const int a, const int b)
    {
        return dis[a] > dis[b];
    }
};
priority_queue<int, vector<int>, cmp> q;

//好像SPAF啊
void Dijkstra(int s)
{
    while(!q.empty())
        q.pop();
    memset(dis, 0x3f, sizeof(dis));
    dis[s] = 0;
    q.push(s);
    int u;
    //理論上用Dijkstra的題不存在負權邊,即每次選出來的邊即爲最短邊,
    //因此每一個點不可能被屢次鬆弛,入隊最多一次
    //因此那個 vis 應該也是多餘的
    while(!q.empty())
    {
        u = q.top();
        q.pop();
        for(int i = head[u];i != -1;i = edge[i].next)
        {
            if(dis[edge[i].to] > dis[u] + edge[i].w)
            {
                dis[edge[i].to] = dis[u] + edge[i].w;
                q.push(edge[i].to);
            }
        }
    }
}

void Add(int a, int b, int c)
{
    edge[++cnt].to = b;
    edge[cnt].w = c;
    edge[cnt].next = head[a];
    head[a] = cnt;
}

int main()
{
    while(cin >> n >> m)
    {
        MemI(head);
        cnt = 0;
        if(!n && !m)
            break;
        int a, b, c;
        for(int i = 0;i < m;++i)
        {
            cin >>  a >> b >> c;
            Add(a, b, c);
            Add(b, a, c);
        }
        Dijkstra(1);
        cout << dis[n] << endl;
    }
    return 0;
}

6)SPFA算法兩種優化(SLF,LLL)

https://blog.csdn.net/liangzhaoyang1/article/details/62423135

SLF:Small Label First 策略,設要加入的節點是j,隊首元素爲i,若dist(j)<dist(i),則將j插入隊首,不然插入隊尾。

LLL:Large Label Last 策略,設隊首元素爲i,隊列中全部dist值的平均值爲x,若dist(i)>x則將i插入到隊尾,查找下一元素,直到找到某一i使得dist(i)<=x,則將i出對進行鬆弛操做。 SLF 可以使速度提升 15 ~ 20%;SLF + LLL 可提升約 50%。 在實際的應用中SPFA的算法時間效率不是很穩定,爲了不最壞狀況的出現,一般使用效率更加穩定的Dijkstra算法。

雙端隊列插入頭尾的操做而已

相關文章
相關標籤/搜索