1. 程式人生 > >談一談Dijkstra

談一談Dijkstra

highlight wid eight tex targe AS tdi CP ems

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]$

表示從起點到i節點的距離,我們在程序一開始的時候把dis全部賦值成INF,

只把dis[s]賦值成0,(因為s->s == 0),這時候我們需要定義一個bool行的數組book,book[i]表示節點是否被訪問過。

那麽接下來就進入核心部分了。

我們每次從所有的節點中找一個dis值最小的,當讓我們知道第一次肯定會找到s,然後以這個點為節點向外擴展,如果在已知的邊的基礎上可以找到更短的到某一個點的路徑,那麽就將這個路徑更新。

這麽說可能有點抽象,那麽讓我們看點實在的

上圖

技術分享圖片

第一次找到了dis值最小的1號節點向外擴展,那麽現在dis數組的值為

$dis[1] = 0, dis[2] = INF,

dis[3] = 3, dis[4] = 4, dis[5] = INF, dis[6] = 10$

記得要把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