1. 程式人生 > >最短路——迪傑斯特拉演算法

最短路——迪傑斯特拉演算法

1.在圖論中經常會碰到最短路問題。最短路顧名思義就是在有向連通圖(也就是連通網)中尋找兩點之間的最短距離。為了解決這個問題人們提出了很多行之有效的演算法,這其中有著名的五大演算法:Dijkstra演算法,SPFA演算法,Floyd演算法,Bellman-Ford演算法,以及基於啟發式搜尋的A*演算法。我們接下來會學習每一個演算法,首先是DJ演算法。

2.DJ演算法是一種求解單源最短路非負權值的有效方法。基本思想:首先,建立兩個集合,以集合中的每一個點作為研究物件。第一個集合我們叫做S,第二個叫做D。初始化的時候,S內放我們的源點,D內放除源點之外的其他點。(what?什麼叫源點?源點就是起點唄,這裡的源有源頭的意思,這個定義在網路流的中會更加貼切,我們這裡暫時把它理解為起點)。我們再來看一下我們的問題,我們需要找一個點到另一個點的最短路。所以S內起始只有一個元素,D內有n-1個元素(假設有n個點)。初始化完成以後,我們開始做事情。分為三步:

(1)首先,我們在D中掃描,找出D中的每一個點到源點的距離,如果二者沒有直接相連的邊,這個距離被設定成INF。所以這裡我們就需要一個二維陣列儲存每兩個連通點之間的距離,還要有一個數組,來標記這個點已經被訪問過。

(2)掃描之後,找到了距離源點最近的點,將該點加入S中。

(3)重點來了!我們先給出定義,下邊將詳細講解這個地方,這也是理解DJ的關鍵所在。繼續執行(1)(2),這個時候,在D中尋找距離第二個點最近的點,並使用第二個點更新距離陣列。反覆執行,直到D為空集。

我們來理解一下3,上一張

其實第三步是著名的鬆弛技術:基於三角不等式來的。if : dis[j]>dis]i]+map[s][j],可以將dis[j]更新。我們對著上邊的圖解釋一下:

起始時:A到B,C,D的距離是:10,INF,20.注意我們要尋找的是A到C的最短路徑。所以我們的dis陣列執行完以後就會被更新成這樣的情況:dis[B]=10,dis[C]=INF,dis[D]=20.這個時候,我們將B加入集合S。最最精華的部分來了:現在B加入以後,我們繼續更新dis陣列,我們發現,A雖然不能直接到C,但是C可以從B來,也就是上邊的不等式。我們發現dis[C]>dis[B]+map[B][C],OK,那麼我們就更新dis[C],這樣就達到了一種鬆弛的效果。繼續做這些操作,每一個點的任務都是用來鬆弛源點到終點的距離,這樣我們通過一種貪心的思想就可以達到最優的效果。下邊給出HDU2544最短路裸題的程式碼:

#include"stdafx.h"
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<stdio.h>
using namespace std;
#define N 110
#define MAX 999999
int nodenum, edgenum;
int map[N][N], dis[N];
bool visit[N];
//最短路的DJ演算法
int Dijkstra(int src, int des)
{
	int temp, k;
	//初始化visit陣列
	for (int i = 0; i < sizeof(visit); i++)
	{
		visit[i] = false;
	}
	//初始化邊集
	for (int i = 1; i <= nodenum; ++i)
		dis[i] = (i == src ? 0 : map[src][i]);
	visit[src] = true;
	dis[src] = 0;
	//貪心尋找最短路,並且通過該點鬆弛其他邊
	for (int i = 1; i <= nodenum; ++i)
	{
		temp = MAX;
		for (int j = 1; j <= nodenum; ++j)
			if (!visit[j] && temp > dis[j])
				temp = dis[k = j];
		if (temp == MAX)
			break;
		visit[k] = true;
		for (int j = 1; j <= nodenum; ++j)
			if (!visit[j] && dis[j] > dis[k] + map[k][j])
				dis[j] = dis[k] + map[k][j];
	}
	return dis[des];
}

int main()
{
	int start, end, cost;
	int answer;
	while (~scanf("%d%d", &nodenum, &edgenum) && (nodenum + edgenum))
	{
		for (int i = 1; i <= nodenum; ++i)
			for (int j = 1; j <= nodenum; ++j)
				map[i][j] = MAX;
		for (int i = 1; i <= edgenum; ++i)
		{
			scanf("%d%d%d", &start, &end, &cost);
			if (cost < map[start][end])
				map[start][end] = map[end][start] = cost;
		}
		answer = Dijkstra(1, nodenum);//源點到終點的最短路
		printf("%d\n", answer);
	}
	return 0;
}