1. 程式人生 > >【最小費用最大流】知識點講解

【最小費用最大流】知識點講解

概念

在同一個網路中,可能存在多個總流量相同的最大流,我們可以在計算流量的基礎之上,給網路中的弧增加一個單位流量費用(簡稱費用),在確保流量最大的前提下總費用最小——最小費用最大流。


演算法思路

邊權的資料有兩個,一個是容量,一個是費用。

主要是兩部分,①spfa來求邊權(根據費用w)最短路;②在最短路基礎上統計計算最小費用的。用spfa求出最短路(其中有個pre[i]陣列表示指向i點的邊序號),然後再倒序從終點沿著最短路走反向邊,找到該最短路上的最小邊權——流量。然後再來走一遍,這次需要每次都把最小費用統計一下(+=最小流量*費用),同時從當前邊權容量上減去當前流量,以便進入下一次迴圈的spfa。

const int MAX_N = 1000;
const int MAX_M = 10000;
const int inf = 0x3f3f3f3f;

struct edge {
    int v, c, w, next;  // v 表示邊的另一個頂點,c 表示當前剩餘容量,w 表示單位流量費用
} e[MAX_M];
int p[MAX_N], s, t, eid;  // s 表示源點,t 表示匯點,需要在進行 costflow 之前設定完畢
void init() {
    memset(p, -1, sizeof(p));
    eid = 0;
}
void insert(int u, int v, int c, int w) {
    e[eid].v = v;
    e[eid].c = c;
    e[eid].w = w;
    e[eid].next = p[u];
    p[u] = eid++;
}
void addedge(int u, int v, int c, int w) {
    insert(u, v, c, w);
    insert(v, u, 0, -w);
}
bool inq[MAX_N];
int d[MAX_N];  // 如果到頂點 i 的距離是 0x3f3f3f3f,則說明不存在源點到 i 的最短路
int pre[MAX_N];  // 最短路中連向當前頂點的邊的編號
bool spfa() {  // 以源點 s 為起點計算單源最短路,如果不存在從 s 到 t 的路徑則返回 false,否則返回 true
    memset(inq, 0, sizeof(inq));
    memset(d, 0x3f, sizeof(d));
    memset(pre, -1, sizeof(pre));
    d[s] = 0;
    inq[s] = true;
    queue<int> q;
    q.push(s);
    while (!q.empty()) {
        int u = q.front();
        q.pop();
        inq[u] = false;
        for (int i = p[u]; i != -1; i = e[i].next) {
            if (e[i].c) {    //注意這個條件!!!spfa這裡是以w求最短路的,但仍然不能忽略容量c的考慮!
                int v = e[i].v;
                if (d[u] + e[i].w < d[v]) {
                    d[v] = d[u] + e[i].w;
                    pre[v] = i;
                    if (!inq[v]) {
                        q.push(v);
                        inq[v] = true;
                    }
                }
            }
        }
    }
    return pre[t] != -1;
}

int costflow() {  // 計算最小費用最大流
    int ret = 0;  // 累加和
    while(spfa()) {
        int flow = inf;
        for(int i = t; i != s; i = e[pre[i]^1].v) {
            flow = min(e[pre[i]].c, flow);  // 計算當前增廣路上的最小流量
        }
        for(int i = t; i != s; i = e[pre[i]^1].v) {
            e[pre[i]].c -= flow;     //容量是一定要跟著變化的,畢竟要繼續迴圈使用spfa來更新下一條“能走的(看容量)”最短路。
            e[pre[i]^1].c += flow;
            ret += e[pre[i]].w * flow;
        }
    }
    return ret;
}