2011-07-21 10:51:19Morris
A-Star Algorithm 單源第k短路徑
A* Algorithm 單純求最短路徑, 啟發 H(x) 並不好估計, 但是求 第 k 短, 它就派得上用場了
內容轉至 http://www.cppblog.com/MatoNo1/archive/2011/05/01/145456.html
【问题描述】
给出一个图G和指定的源点s、汇点t,求图中从点s到点t的第K短路。
【算法】
本题有一种最朴素的办法:改造Dijkstra,一开始路径(s, 0)(这里设路径(i, l)表示从s到i的一条长度为l的路径)入队。然后,每次取队中长度最短的路径,该路径(i, l)出队,可以证明,若这是终点为i的路径第x次出队,该路径一定是图中从s到i的第x短路(若x>K则该路径已无用,舍弃)。然后从点i扩展,将扩展到的路径全部入队。这样直到终点为t的路径第K次出队即可。
该算法容易实现(借助priority_queue),但时间复杂度可能达到O(MK),需要优化。
优化:容易发现该算法其实有A*的思想,或者说,该算法其实是所有结点的估价函数h()值均为0的A*算法。为了优化此题,需要将h()值改大。显然,h(i)值可以设为从i到t的最短路径长度(容易证明它是一致的),然后g(i)=目前结点代表的路径长度,f(i)=g(i)+h(i),然后A*即可。
注意:更改路径条数应该在出队时更改,而不能在入队时更改,因为可能在该路径出队之前会有新的比它更短的路径入队。
以下是我在 ZeroJudge 的實踐
/**********************************************************************************/
/* Problem: d243 "圖論專家" from */
/* Language: C */
/* Result: AC (3ms, 304KB) on ZeroJudge */
/* Author: morris1028 at 2011-07-21 10:41:43 */
/**********************************************************************************/
#include<stdio.h>
#define MaxV 1000000
typedef struct Link {
int x, y, w;
}Link;
struct Heap {
int v, node;
}Heap[1001];/*min heap*/
int L;
void Swap(int, int);
void siftup(int);
void siftdown(int);
void insHeap(int, int, int);
void delHeap();
void MergeSort(int, int, Link[]);
void Merge(int, int, int, Link[]);
Link Data[1001], X[1001], RevD[1001];
int NodeT[1001], NodeL[1001];
int RevNT[1001], RevNL[1001];
int Hdis[1001], Gdis[1001];/*f(x) = g(x) + h(x)*/
void prepare(int st, int ed, int n) { /*SPFA*/
int a, b, c;
int Queue[n*n], Qt = 0, Used[n], tn;
for(a = 0; a < n; a++) Hdis[a] = MaxV, Used[a] = 0;
Queue[0] = ed, Used[ed] = 1, Hdis[ed] = 0;
for(a = 0; a <= Qt; a++) {
tn = Queue[a], Used[tn] = 0;
for(b = RevNL[tn], c = 0; c < RevNT[tn]; b++, c++) {
if(Hdis[tn] + RevD[b].w < Hdis[RevD[b].y]) {
Hdis[RevD[b].y] = Hdis[tn] + RevD[b].w;
if(Used[RevD[b].y] == 0) {
Used[RevD[b].y] = 1, Queue[++Qt] = RevD[b].y;
}
}
}
}
}
void solve_kpath(int st, int ed, int k, int n) {
L = 0;
int OutT[n], a, b, tn, tw;
for(a = 0; a < n; a++) OutT[a] = 0, Gdis[n] = MaxV;
for(a = NodeL[st], b = 0; b < NodeT[st]; a++, b++) {
L++, insHeap(L, Data[a].y, Data[a].w + Hdis[Data[a].y]);
}
OutT[st]++;
while(L) {
tn = Heap[1].node, tw = Heap[1].v;
delHeap();
if(OutT[tn] >= k || tw == Gdis[tn]) continue;
Gdis[tn] = tw, OutT[tn]++, tw = tw - Hdis[tn];
if(OutT[tn] == k && tn == ed) {printf("%d\n", tw);return;}
for(a = NodeL[tn], b = 0; b < NodeT[tn]; a++, b++) {
L++, insHeap(L, Data[a].y, tw + Data[a].w + Hdis[Data[a].y]);
}
}
puts("?");
}
main() {
int n, m, a, b, C = 0;
int x, y, d, p, k, st, ed;
while(scanf("%d %d", &n, &m) == 2) {
int rt, t;
for(a = 0; a < n; a++)/*init*/
NodeT[a] = RevNT[a] = 0, NodeL[a] = RevNL[a] = MaxV;
for(a = 0, t = 0, rt = 0; a < m; a++) {
scanf("%d %d %d", &x, &y, &d);
Data[t].x = x, Data[t].y = y, Data[t].w = d, t++;
Data[t].x = y, Data[t].y = x, Data[t].w = d, t++;
NodeT[x]++, NodeT[y]++;
RevD[rt].x = y, RevD[rt].y = x, RevD[rt].w = d, rt++;
RevD[rt].x = x, RevD[rt].y = y, RevD[rt].w = d, rt++;
RevNT[y]++, RevNT[x]++;
}
MergeSort(0, t-1, Data);
MergeSort(0, rt-1, RevD);
for(a = 0; a < t; a++) {
NodeL[Data[a].x] = NodeL[Data[a].x] < a ? NodeL[Data[a].x]: a;
RevNL[RevD[a].x] = RevNL[RevD[a].x] < a ? RevNL[RevD[a].x]: a;
}
scanf("%d", &p);
printf("Set #%d\n", ++C);
while(p--) {
scanf("%d %d %d", &st, &ed, &k);
prepare(st, ed, n);
solve_kpath(st, ed, k, n);
}
}
return 0;
}
void Swap(int P, int S) {
int T;
T=Heap[P].v, Heap[P].v=Heap[S].v, Heap[S].v=T;
T=Heap[P].node, Heap[P].node=Heap[S].node, Heap[S].node=T;
}
void siftup (int site) {
int S = site, P = site >> 1;
while(S >= 2 && Heap[P].v > Heap[S].v)
Swap(P, S), S = P, P = S >> 1;
}
void siftdown(int site) {
int P = site, S = site << 1;
while(S <= L) {
if(S < L && Heap[S+1].v < Heap[S].v) S++;
if(Heap[P].v <= Heap[S].v) break;
Swap(P, S), P = S, S = P << 1;
}
}
void insHeap(int site, int node, int t) {/*insert new node*/
Heap[site].node = node, Heap[site].v = t;
siftup(site);
}
void delHeap() {/*delete last node*/
Swap(1, L), L--;
siftdown(1);
}
void MergeSort(int l, int h, Link Data[]) {
if(l < h) {
int m = (l+h)/2;
MergeSort(l, m, Data);
MergeSort(m+1, h, Data);
Merge(l, m, h, Data);
}
}
void Merge(int l, int m, int h, Link Data[]) {
int In1 = l, In2 = m+1;
int a, b, Top = 0;
while(In1 <= m && In2 <= h) {
if(Data[In1].x < Data[In2].x || (Data[In1].x == Data[In2].x && Data[In1].w <= Data[In2].w))
X[Top++] = Data[In1++];
else
X[Top++] = Data[In2++];
}
while(In1 <= m) X[Top++] = Data[In1++];
while(In2 <= h) X[Top++] = Data[In2++];
for(a = 0, b = l; a < Top; a++, b++)
Data[b] = X[a];
}
內容轉至 http://www.cppblog.com/MatoNo1/archive/2011/05/01/145456.html
【问题描述】
给出一个图G和指定的源点s、汇点t,求图中从点s到点t的第K短路。
【算法】
本题有一种最朴素的办法:改造Dijkstra,一开始路径(s, 0)(这里设路径(i, l)表示从s到i的一条长度为l的路径)入队。然后,每次取队中长度最短的路径,该路径(i, l)出队,可以证明,若这是终点为i的路径第x次出队,该路径一定是图中从s到i的第x短路(若x>K则该路径已无用,舍弃)。然后从点i扩展,将扩展到的路径全部入队。这样直到终点为t的路径第K次出队即可。
该算法容易实现(借助priority_queue),但时间复杂度可能达到O(MK),需要优化。
优化:容易发现该算法其实有A*的思想,或者说,该算法其实是所有结点的估价函数h()值均为0的A*算法。为了优化此题,需要将h()值改大。显然,h(i)值可以设为从i到t的最短路径长度(容易证明它是一致的),然后g(i)=目前结点代表的路径长度,f(i)=g(i)+h(i),然后A*即可。
注意:更改路径条数应该在出队时更改,而不能在入队时更改,因为可能在该路径出队之前会有新的比它更短的路径入队。
以下是我在 ZeroJudge 的實踐
/**********************************************************************************/
/* Problem: d243 "圖論專家" from */
/* Language: C */
/* Result: AC (3ms, 304KB) on ZeroJudge */
/* Author: morris1028 at 2011-07-21 10:41:43 */
/**********************************************************************************/
#include<stdio.h>
#define MaxV 1000000
typedef struct Link {
int x, y, w;
}Link;
struct Heap {
int v, node;
}Heap[1001];/*min heap*/
int L;
void Swap(int, int);
void siftup(int);
void siftdown(int);
void insHeap(int, int, int);
void delHeap();
void MergeSort(int, int, Link[]);
void Merge(int, int, int, Link[]);
Link Data[1001], X[1001], RevD[1001];
int NodeT[1001], NodeL[1001];
int RevNT[1001], RevNL[1001];
int Hdis[1001], Gdis[1001];/*f(x) = g(x) + h(x)*/
void prepare(int st, int ed, int n) { /*SPFA*/
int a, b, c;
int Queue[n*n], Qt = 0, Used[n], tn;
for(a = 0; a < n; a++) Hdis[a] = MaxV, Used[a] = 0;
Queue[0] = ed, Used[ed] = 1, Hdis[ed] = 0;
for(a = 0; a <= Qt; a++) {
tn = Queue[a], Used[tn] = 0;
for(b = RevNL[tn], c = 0; c < RevNT[tn]; b++, c++) {
if(Hdis[tn] + RevD[b].w < Hdis[RevD[b].y]) {
Hdis[RevD[b].y] = Hdis[tn] + RevD[b].w;
if(Used[RevD[b].y] == 0) {
Used[RevD[b].y] = 1, Queue[++Qt] = RevD[b].y;
}
}
}
}
}
void solve_kpath(int st, int ed, int k, int n) {
L = 0;
int OutT[n], a, b, tn, tw;
for(a = 0; a < n; a++) OutT[a] = 0, Gdis[n] = MaxV;
for(a = NodeL[st], b = 0; b < NodeT[st]; a++, b++) {
L++, insHeap(L, Data[a].y, Data[a].w + Hdis[Data[a].y]);
}
OutT[st]++;
while(L) {
tn = Heap[1].node, tw = Heap[1].v;
delHeap();
if(OutT[tn] >= k || tw == Gdis[tn]) continue;
Gdis[tn] = tw, OutT[tn]++, tw = tw - Hdis[tn];
if(OutT[tn] == k && tn == ed) {printf("%d\n", tw);return;}
for(a = NodeL[tn], b = 0; b < NodeT[tn]; a++, b++) {
L++, insHeap(L, Data[a].y, tw + Data[a].w + Hdis[Data[a].y]);
}
}
puts("?");
}
main() {
int n, m, a, b, C = 0;
int x, y, d, p, k, st, ed;
while(scanf("%d %d", &n, &m) == 2) {
int rt, t;
for(a = 0; a < n; a++)/*init*/
NodeT[a] = RevNT[a] = 0, NodeL[a] = RevNL[a] = MaxV;
for(a = 0, t = 0, rt = 0; a < m; a++) {
scanf("%d %d %d", &x, &y, &d);
Data[t].x = x, Data[t].y = y, Data[t].w = d, t++;
Data[t].x = y, Data[t].y = x, Data[t].w = d, t++;
NodeT[x]++, NodeT[y]++;
RevD[rt].x = y, RevD[rt].y = x, RevD[rt].w = d, rt++;
RevD[rt].x = x, RevD[rt].y = y, RevD[rt].w = d, rt++;
RevNT[y]++, RevNT[x]++;
}
MergeSort(0, t-1, Data);
MergeSort(0, rt-1, RevD);
for(a = 0; a < t; a++) {
NodeL[Data[a].x] = NodeL[Data[a].x] < a ? NodeL[Data[a].x]: a;
RevNL[RevD[a].x] = RevNL[RevD[a].x] < a ? RevNL[RevD[a].x]: a;
}
scanf("%d", &p);
printf("Set #%d\n", ++C);
while(p--) {
scanf("%d %d %d", &st, &ed, &k);
prepare(st, ed, n);
solve_kpath(st, ed, k, n);
}
}
return 0;
}
void Swap(int P, int S) {
int T;
T=Heap[P].v, Heap[P].v=Heap[S].v, Heap[S].v=T;
T=Heap[P].node, Heap[P].node=Heap[S].node, Heap[S].node=T;
}
void siftup (int site) {
int S = site, P = site >> 1;
while(S >= 2 && Heap[P].v > Heap[S].v)
Swap(P, S), S = P, P = S >> 1;
}
void siftdown(int site) {
int P = site, S = site << 1;
while(S <= L) {
if(S < L && Heap[S+1].v < Heap[S].v) S++;
if(Heap[P].v <= Heap[S].v) break;
Swap(P, S), P = S, S = P << 1;
}
}
void insHeap(int site, int node, int t) {/*insert new node*/
Heap[site].node = node, Heap[site].v = t;
siftup(site);
}
void delHeap() {/*delete last node*/
Swap(1, L), L--;
siftdown(1);
}
void MergeSort(int l, int h, Link Data[]) {
if(l < h) {
int m = (l+h)/2;
MergeSort(l, m, Data);
MergeSort(m+1, h, Data);
Merge(l, m, h, Data);
}
}
void Merge(int l, int m, int h, Link Data[]) {
int In1 = l, In2 = m+1;
int a, b, Top = 0;
while(In1 <= m && In2 <= h) {
if(Data[In1].x < Data[In2].x || (Data[In1].x == Data[In2].x && Data[In1].w <= Data[In2].w))
X[Top++] = Data[In1++];
else
X[Top++] = Data[In2++];
}
while(In1 <= m) X[Top++] = Data[In1++];
while(In2 <= h) X[Top++] = Data[In2++];
for(a = 0, b = l; a < Top; a++, b++)
Data[b] = X[a];
}