談一談Dijkstra
dijkstra呢是最短路三大算法之一。很多人都覺得不如spfa,但是這兩者在跑稠密圖時,dijkstra有奇效
在講之前先說一說食用方法:
適用於有向的無負權值的圖。
樣例飄過
6 9 1 //n個點,m條邊,以s為起點 1 3 3 1 6 10 3 6 5 4 3 7 1 4 4 4 2 5 5 2 4 4 5 6 3 5 6
0 9 3 4 9 8
上面這組樣例我們讓他更直觀一些
神圖警報,請開啟護眼模式
真心累
首先我們應該知道dijkstra的核心思想是貪心。
定義一個$dis$數組,$dis[i]$
只把dis[s]賦值成0,(因為s->s == 0),這時候我們需要定義一個bool行的數組book,book[i]表示節點是否被訪問過。
那麽接下來就進入核心部分了。
我們每次從所有的節點中找一個dis值最小的,當讓我們知道第一次肯定會找到s,然後以這個點為節點向外擴展,如果在已知的邊的基礎上可以找到更短的到某一個點的路徑,那麽就將這個路徑更新。
這麽說可能有點抽象,那麽讓我們看點實在的
上圖
第一次找到了dis值最小的1號節點向外擴展,那麽現在dis數組的值為
$dis[1] = 0, dis[2] = INF,
記得要把book[1]變成1,代表已經訪問過。
接下來進行第二次擴展,這時候由於dis[1]我們已經訪問過,所以接下來就會選擇3號節點進行擴展
如圖
從3號節點出去的邊可以連到5和6,連5時毫無疑問會更新,但是再連6時,我們發現dis[6]已經擴展了一次,那麽怎麽辦呢,很明顯,從1走到3再走到6的話總路程為dis[3]加上3到6的路程,共為8,比現有的dis[6]要小,所以我們就要再更新dis[6] = 8;
通過這麽一次次的更新,我們就能把這張圖的最短路跑完,註意是單源最短路。
說了這麽多我們來看下代碼,但是我這個代碼有個很玄學的地方,就是建邊的時候我寫的是用數組實現的鄰接鏈表,但是原理是和結構體實現的鄰接鏈表相同,大家將就著看,如果看不懂那就參考一下下面這篇文章
【坐在馬桶上看算法】算法8:巧妙的鄰接表(數組實現)
這裏講的非常的詳細,我當初就是看的這裏。
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define MAXN 500008 #define INF 2147483647 #define maxn 10008 using namespace std; int n, m, s; int next[MAXN], first[MAXN]; int u[MAXN], v[MAXN], w[MAXN]; int dis[maxn]; bool book[MAXN]; int main() { scanf("%d%d%d", &n, &m, &s); for(int i=1; i<=n; i++) { dis[i] = INF; } dis[s] = 0; memset(first, -1, sizeof(first)); for(int i=1; i<=m; i++) { scanf("%d%d%d", &u[i], &v[i], &w[i]); next[i] = first[u[i]]; first[u[i]] = i; } for(int i=1; i<n; i++) { int minn = INF, x; for(int j=1; j<=n; j++) { if(dis[j] < minn&&book[j] == 0) { minn = dis[j]; x = j; } } book[x] = 1; int k = first[x]; while(k != -1) { if(dis[v[k]] > dis[u[k]]+w[k]) { dis[v[k]] = dis[u[k]]+w[k]; } k = next[k]; } } for(int i=1; i<=n; i++) { printf("%d ", dis[i]); } }
談一談Dijkstra