終於調出來了,理解之後其實很簡單,但是一些細節總是沒處理好。直到現在還有一點疑問在。
兩者的漸進複雜度都是O(ELogV+VLogV),因為邊調整了E次,刪除最小元V次,二元堆積的操作都是LogV層級的。
事實上兩個演算法很相似,Prim只是鬆弛操作和Dijksrra還有記錄ans的方式不同。
在演算法導論上看到,Kruskal演算法是ELogE的即可以約成E*2LogV即ELogV,漸進意義上是和Prim一樣的。
平時很少看見用heap最佳化的Prim和Dijkstra,可能是因為Kruskal和SPFA有更好的表現或編程複雜度小點吧,當然也看到不少C++用STL的優先隊列寫的Prim/Dijkstra。
下面是POJ 2387和1287的代碼,分別對應Dijkstra和Prim演算法。
/* 很裸很水的最短路,練習二元堆積最佳化的Dijstra~ 之前二元堆積最佳化的Prim敲了好幾遍前後花了不下八個小時調試還是沒有調試成功, 但是還好,熟悉了優先隊列的操作。 十幾天后的今天重新想起這個,終於調出來了堆最佳化的Dijstra。理解之後還是蠻簡單的。 一些寫法並不是最優的,例如heap的實現中可以減少交換元素等。但是有這個自己寫的AC 過的Dijkstra在,以後寫二元堆積最佳化的Prim/Dijkstra和其它優先隊列的題目就可以拿它對照著Debug了。 2011-07-24 23:00*/#include <stdio.h>#define MAXN 1200#define MAXM 1200000#define INF 19930317struct node{ int d, v, p;}heap[MAXN];int pos[MAXN], hl;int e[MAXM], cost[MAXM], next[MAXM], g[MAXN], size;int m, n, s, t;void insert(int u, int v, int w){ e[++size] = v; next[size] = g[u]; cost[size] = w; g[u] = size;}void swap(int a, int b){ heap[0] = heap[a]; heap[a] = heap[b]; heap[b] = heap[0]; pos[heap[a].v] = a; pos[heap[b].v] = b;}void heapfy(){ int i = 2; while (i <= hl) { if ((i < hl) && (heap[i + 1].d < heap[i].d)) i++; if (heap[i].d < heap[i >> 1].d) { swap(i, i >> 1); i <<= 1; } else break; }}void decrease(int i){ while ((i != 1) && (heap[i].d < heap[i >> 1].d)) { swap(i, i >> 1); i >>= 1; }}void relax(int u ,int v, int w){ if (w + heap[pos[u]].d < heap[pos[v]].d) { heap[pos[v]].p = u; heap[pos[v]].d = w + heap[pos[u]].d; decrease(pos[v]); }}void delete_min(){ swap(1, hl); hl--; heapfy();}void init(){ int u ,v ,w, i; scanf("%d%d", &m, &n); for (i = 1; i <= m; i++) { scanf("%d%d%d", &u, &v, &w); insert(u, v, w); insert(v, u, w); } s = 1; t = n;}int dijkstra(){ int u, p, i; for (i = 1; i <= n; i++) { heap[i].v = pos[i] = i; heap[i].d = INF; } heap[s].p = s; heap[s].d = 0; swap(1, s); hl = n; while (hl) { u = heap[1].v; delete_min(); p = g[u]; while (p) { if (pos[e[p]] <= hl) relax(u, e[p], cost[p]); p = next[p]; } }}int main(){ init(); dijkstra(); printf("%d\n", heap[pos[t]].d); return 0;}
/* 二元堆積最佳化的Prim,相比Dijkstra只有鬆弛操作不同 很鬱悶的是heapfy函數,就是從堆頂往下篩,仿照別人的寫法AC了,自己寫的迭代以減少 交換次數的在範例的一個資料陷入死迴圈了,而仿照資料結構與演算法分析寫的WA了。 以前沒寫過帶有pos作為優先隊列的heap這樣調整,所以也不知道哪錯了。 下面是強大的範例:5 71 2 52 3 72 4 84 5 113 5 101 5 64 2 12 有空調調吧。 2011-07-25 09:32*/#include <stdio.h>#define MAXN 1200#define MAXM 120000#define INF 19930317int e[MAXM], cost[MAXM], next[MAXM], g[MAXN], size;struct node{ int d, v;}heap[MAXN];int pos[MAXN], hl;int m, n;void swap(int a, int b){ heap[0] = heap[a]; heap[a] = heap[b]; heap[b] = heap[0]; pos[heap[a].v] = a; pos[heap[b].v] = b;}void heapfy(){ int i = 2; while (i <= hl) { if ((i < hl) && (heap[i + 1].d < heap[i].d)) i++; if (heap[i].d < heap[i >> 1].d) { swap(i, i >> 1); i <<= 1; } else break; }}/*void heapfy(){ int i = 1, j = 2; heap[0] = heap[1]; while (j <= hl) { if ((j < hl) && (heap[j].d > heap[j+1].d)) j++; if (heap[j].d < heap[0].d) { heap[i] = heap[j]; pos[heap[i].v] = i; i = j; j >>= 1; } else break; } heap[i] = heap[0]; pos[heap[i].v] = i;}void heapfy(){ int i, j; heap[0] = heap[1]; for (i = 1; i * 2 <= hl; i = j) { j = i * 2; if (j < hl && heap[j+1].d < heap[j].d) j++; if (heap[0].d > heap[j].d) { heap[i] = heap[j]; pos[heap[i].v] = i; } } heap[i] = heap[0]; pos[heap[i].v] = i;}*/void decrease(int i){ heap[0] = heap[i]; while ((i > 1) && (heap[0].d < heap[i >> 1].d)) { heap[i] = heap[i >> 1]; pos[heap[i].v] = i; i >>= 1; } heap[i] = heap[0]; pos[heap[i].v] = i;}void delete_min(){ swap(1, hl); hl--; heapfy();}void relax(int v, int w){ if (w < heap[pos[v]].d) { heap[pos[v]].d = w; decrease(pos[v]); }}void insert(int u, int v, int w){ e[++size] = v; cost[size] = w; next[size] = g[u]; g[u] = size;}void init(){ int i, u, v, w; size = 0; memset(g, 0, sizeof(g)); for (i = 1; i <= m; i++) { scanf("%d%d%d", &u, &v, &w); insert(u, v, w); insert(v, u, w); }}int prim(){ int mst = 0, u , p, i; for (i = 1; i <= n; i++) { pos[i] = heap[i].v = i; heap[i].d = INF; } heap[1].d = 0; hl = n; while (hl) { u = heap[1].v; mst += heap[1].d; delete_min(); for (p = g[u]; p; p = next[p]) if (pos[e[p]] <= hl) relax(e[p], cost[p]); } return mst;}int main(){ while (scanf("%d%d", &n, &m) == 2 && n) { init(); printf("%d\n", prim()); }}