1. 程式人生 > >一個我認為比較好的Spfa模板(使用鄰接表和佇列實現)

一個我認為比較好的Spfa模板(使用鄰接表和佇列實現)

全域性準備工作
int N, X;	//N為點數 X為源點
int head[MAXN];	//head[src]表示以head為出發點的鄰接表表頭在陣列Adj中的位置,開始時所有元素初始化為-1
int nodeP;	//在鄰接表和指向表頭的head陣列中定位用的記錄指標,開始時初始化為0
int dist[MAXN];	//儲存到源節點的距離,在Spfa()中初始化
bool vis[MAXN];	//這裡vis作inqueue解釋會更好,出於習慣使用了vis來命名,在Spfa()中初始化
Node定義
struct Node {
    int v, w, next;
}Adj[MAXM];
v即vertex,這裡意思相當於被指向的點to,w相當於weight,代表邊權,next用來指向在Adj中下一個從相同src出發的邊指向的點 addEdge函式
void addEdge(int src, int to, int weight)
{
    Adj[nodeP].v = to;
    Adj[nodeP].w = weight;
    Adj[nodeP].next = head[src];
    head[src] = nodeP++;
}
addEdge(a, b, w)作用是在陣列Adj中留下一條記錄了去向和邊權的記錄,且其next指向表中相同src指向的下一個點,同時更新了head表頭 Spfa主體
void Spfa()
{
    queue<int> que;
    int i;
    for(i = 1; i <= N; i++) {
        dist[i] = NIL;
        vis[i] = false;
    }
    dist[X] = 0;	//X為源點
    que.push(X);
    while(!que.empty()) {
        int now = que.front();
        que.pop();
        vis[now] = false;	//從queue中退出
	//遍歷鄰接表
        for(i = head[now]; i != -1; i = Adj[i].next) {	//在Adj中,相同src出發指向的頂點為從head[src]開始的一項,逐項使用next尋找下去,直到找到第一個被輸
                                                        //入的項,其next值為-1
            int to = Adj[i].v;				
            if(dist[to] == NIL || dist[to] > dist[now] + Adj[i].w) {	//鬆弛(RELAX)操作
                dist[to] = dist[now] + Adj[i].w;
                if(!vis[to]) {	//若被搜尋到的節點不在佇列que中,則把to加入到佇列中去
                    vis[to] = true;
                    que.push(to);
                }
            }
        }
    }
}
main中初始化
int main()
{
    memset(head, -1, sizeof(head));
    ... 
   while(...) {
    	cin >> a >> b >> c;
    	addEdge(a, b, c);
    }
    Spfa();
    //結果已經儲存在dist陣列中
    return 0;
}
有些人以為這個演算法是Dijkstra,事實上Spfa和Dijkstra很像,只是他們維護的佇列不同,Spfa維護的是一個初始只有源節點的佇列,而Dijkstra要維護初始為所有頂點的佇列。