最小費spfa()+ek() 鄰接表

/**
    貼下最小費的鄰接表模板

    圖算法中鄰接表應用極其廣泛,在速度和邊處理方面都要比矩陣好很多

    最小費鄰接矩陣模板前面提到了 就不多說了
    包括添邊等常用的技巧前面也都提到了

    就是求網絡流是會用到逆流邊
    用鄰接表添邊,逆流邊也相當經典,這個技巧剛開始並不好理解

    比如:設edge[p] = ab; 則逆流邊爲 edge[p^1] = ba;

    p^1 這是個什麼東西?? ^ 這個符號是異或,p^1 = ???
    如果p 爲偶數則 p^1 = p+1, 如果p 爲奇數 p^1 = p-1;

    所以p^1 總能得到一個與自己相鄰的數
    把這個現象用到解決逆流邊上面,因爲添邊時候的下一條邊就爲逆流邊
    所以如果從一個偶數開始

    比如:從2開始 edge[2] = ab;  edge[3] = ba; edge[4] = bc; edge[5] = cb;

    每次都會添兩條邊,所以下一條邊開始必爲偶數 p ,而它的逆流邊即爲 p^1

    具體看代碼
*/


struct {
    int v, cap, cost, next;
}edge[eM];  //邊結構體 eM爲 最多有多少條邊

int ne, edgeHead[nM]; //nM爲 點的個數
int pre[nM];          //用來記錄增廣路徑

int ans, ne;

void addEdge(int u, int v, int c, int w) {
    edge[ne].v = v;
    edge[ne].cap = c;
    edge[ne].cost = w;
    edge[ne].next = edgeHead[u];
    edgeHead[u] = ne++;

    edge[ne].v = u;
    edge[ne].cap = 0;
    edge[ne].cost = -w;
    edge[ne].next = edgeHead[v];
    edgeHead[v] = ne++;
}

bool spfa(int s, int t) {
    //spfa算法前面也介紹過,這裏用的棧模式,還有一個隊列版,簡單修改下就能得到
    //一定要記住有時棧會超時,有時隊列也會超時,總有一個能過
    int dis[nM], stack[nM], vis[nM];
    int i, top = 0;
    for (i=0; i<=n; i++) {
        dis[i] = inf;
        vis[i] = false;
    }
    dis[s] = 0;
    stack[++top] = s;
    vis[s] = true;
    while (top) {
        int u = stack[top--];
        for (i=edgeHead[u]; i!=0; i=edge[i].next) {
            int v = edge[i].v;
            if (edge[i].cap && dis[v] > dis[u] + edge[i].cost) {
                dis[v] = dis[u] + edge[i].cost;
                pre[v] = i;     //記錄路徑用的是邊號,所以 u = edge[p^1].v;
                if (!vis[v]) {
                    vis[v] = true;
                    stack[++top] = v;
                }
            }
        }
        vis[u] = false;
    }
    if (dis[t] < inf)   return true;
    return false;
}

void end(int s, int t) {
    //這些原理和鄰接矩陣一模一樣
    int u, p, sum = inf;
    for (u=t; u!=s; u=edge[p^1].v) {
        p = pre[u];
        sum = min(sum, edge[p].cap);
    }
    for (u=t; u!=s; u=edge[p^1].v) {
        //嗯,這個好好模擬下,技巧噢
        p = pre[u];
        edge[p].cap -= sum;
        edge[p^1].cap += sum;
        ans += edge[p].cost * sum;
    }
}

int main() {
    //初始化
    memset(edgeHead, 0, sizeof (edgeHead));
    ne = 2; //一定要從偶數開始,因爲edgeHead 初始化爲0 了所以賦值爲2
    ans = 0; //最小費

    /**
        ***建圖添邊****
        調用addEdge()添邊,uv 表示一條邊,c爲流量,w爲花費
        則 addEdge(u, v, c, w); 即可

        這纔是重重之中的技能
        關鍵在建圖,模板都會套
    */

    //調用
    while (spfa(s, t))
        end(s, t);
    //s爲源點,t爲匯點

    return 0;
}

收藏於 2012-01-12

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章