(說實話,今天實驗室的人去聽宣講會不和我說。之前問也不說在幹什麼,我很生氣。不過算他們錯過了一個‘人才’吧)
dijkstra算法概論:該算法應用在圖論裏面著名的 單源最短路徑問題。
算法適用性:聖經 黑書指出,該算法應用在 無負權,有向圖中。
算法複雜度:事實上,黑書是在圖割、輕邊的概念上展開算法。黑書維護了一個最小優先隊列(以到d值排序),作爲 V - S。 初始化後,每一次迭代(v 次)都對隊列值最小的節點 的每一條出邊進行鬆弛操作。因此維護隊列 涉及到 插入(初始化),彈出(最小值),以及重排序(鬆弛操作)。前兩者對於每一個節點都是一次操作,而重排序(堆的下沉操作)對每一個節點的每一條出邊都要進行操作,所以是e次重排序。
若 按照 白書的代碼 來實現(簡單的數組操作,而不是最小二叉堆),則需要 V^2+E的複雜度。
仍然可以改進。加上稀疏圖和最小二叉堆實現可以降低到,V^2/lgV.。
如果採用非波那契堆來實現,則可以降低到,VlgV+E。
(先回憶到這裏,待續)(Linux下的編輯真是難看,我們繼續)
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
剛剛出去找到了 POJ 1052,這個題目 直接是DAG單源最短路徑問題,而且用最慢的Dj實現也通過了。代碼如下,
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define INF 0xfffffff
#define MAXN 105
int d[MAXN];
int w[MAXN][MAXN];
int v[MAXN];
int n;
void dj(int s){
memset(v, 0, sizeof(v));
for(int i = 0; i < n; i++)
d[i] = ( i == s ? 0 : INF );
for(int i = 0; i < n; i++){
int x, m = INF;
for(int y = 0; y < n; y++)
if( !v[y] && d[y] <= m )
m = d[x=y];
v[x] = 1;
for(int y = 0; y < n; y++)
if( w[x][y] != -1 && d[y] > d[x] + w[x][y])
d[y] = d[x] + w[x][y];
}
}
int s2i(char* str){
if (str[0] == 'x') return -1;
int ret = 0;
while (*str)
ret = ret * 10 + (*str++) - '0';
return ret;
}
int main(){
scanf("%d", &n);
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
w[i][j] = -1;
char input[40];
for(int i = 0; i < n; i++){
w[i][i] = 0;
for(int j = 0; j < i; j++){
scanf("%s", input);
w[i][j] = w[j][i] = s2i(input);
}
}
dj(0);
int output = -1;
for(int i = 1; i < n; i++)
if( d[i] > output ) output = d[i];
printf("%d\n", output);
return 0;
}
關鍵算法在 dj 函數,採用數組實現最小優先隊列(直接暴力遍歷數組)找出最近的節點(輕邊)。然後對 該節點所有 出邊 進行鬆弛操作。每一次找到的(最近節點)都要出列,這裏通過訪問標籤來實現。事實上,在鬆弛操作的時候也可以維護祖先節點,進而在最後得到路徑上的節點序列。
POJ 評判的耗時是0ms 所以 對它改進也沒有意義(這一題 估計就是給新手練習的)。
(我去找一個 有必要 改進隊列實現的題目來,待續)
--------------------------------------------------------------------------------------------------------------------------------------------------------------