網絡流與二分圖

定義##

  • 記圖 G = (V, E)
最大流問題#####
  • 記每條邊所能傳輸的最大流量為 c(e)
  • 記每條邊所能傳輸的最大流量為 f(e)
  • 傳輸量應當滿足以下限制 0 <= f(e) <= c(e)
  • 對于任意除源點和匯點之外的節點應當有流入的流量等于流出的流量。
  • 目標是最大化從源點發出的數據量
割#####
  • 對于某一個頂點集合 S 屬于 V,從 S 出發指向S的外部的那些邊的集合,記為割 (S, V \ S), 這些邊的容量之和稱之為割的容量
最小割問題#####
  • 對于給定網絡, 為了保證沒有從 st 的路徑, 需要刪去的邊的容量的最小值是多少?
匹配#####
  • G 中兩兩沒有公共端點的集合 M 屬于 E
邊覆蓋#####
  • G 中的任意頂點都至少是 F 中某條邊的端點的邊的集合 F 屬于 E
獨立集#####
  • G 中兩兩互不相連的頂點集合 S 屬于 V
頂點覆蓋#####
  • G中的任意邊都有至少一個端點屬于 S 的頂點集合 S 屬于 V

性質##

最大流 = 最小割#####
  • 任意一個流都小于等于任意一個割
  • 構造出一個流(最大流)等于一個割
  • 顯然最大流等于最小割
  • PS:關于這個證明我感覺下面解釋的比較好。

Figure - A minimum cut in the network

We will assume that we are in the situation in which no augmenting path in the network has been found. Let's color in yellow, like in the figure above, every vertex that is reachable by a path that starts from the source and consists of non-full forward edges and of non-empty backward edges. Clearly the sink will be colored in blue, since there is no augmenting path from the source to the sink. Now take every edge that has a yellow starting point and a blue ending point. This edge will have the flow equal to the capacity, otherwise we could have added this edge to the path we had at that point and color the ending point in yellow. Note that if we remove these edges there will be no directed path from the source to the sink in the graph. Now consider every edge that has a blue starting point and a yellow ending point. The flow on this edge must be 0 since otherwise we could have added this edge as a backward edge on the current path and color the starting point in yellow. Thus, the value of the flow must equal the value of the cut, and since every flow is less or equal to every cut, this must be a maximum flow, and the cut is a minimum cut as well.
原文

最大匹配 = 最小頂點覆蓋#####
Figure - C.GIF
  • 記二分圖 G = (U or V, E),在通過最大流求解最大匹配(設最大匹配為K)所得到的殘留網絡中,令 S = (從s不可到達的屬于U的頂點)or(從s可以到達的屬于V的頂點), 則S是G的一個最小頂點覆蓋。
  • |S| = |K|
    我們考慮(從s不可到達的屬于U的頂點 L), 此時s向L連的邊一定是滿流的, 所以它一定會向V匹配一條邊E(L,R), 而此時這條邊的右端點(設為R,R屬于V), 一定是不可到達的, 如果R可以到達, s就可以通過E的反向邊到達L。
    我們考慮(從s可以到達的屬于V的頂點 R), 此時R向t連的邊一定是滿流的, 所以它一定是由V匹配的一條邊E(L, R)得出的, 而此時這條邊的左端點(設為L, L屬于U), 一定是可以到達的(通過反向邊)。
    所以我們得出此時二分圖匹配的邊和S中的頂點一一對應, 所以|S| = |K|。
  • S覆蓋了所有的邊。
    我們假設有的邊沒有被覆蓋, 則它的端點分為三種情況。
  • 兩個端點都不屬于最大匹配
    把這條邊加進最大匹配可以得到更優的匹配, 顯然不可能。
  • 兩個端點都是最大匹配的端點
    從殘量網絡的角度考慮顯然這條邊屬于最大匹配。
  • 一個端點是最大匹配的端點, 另一個端點不是
    簡便起見, 我們規定左端點是最大匹配的端點而右端點不是。
    顯然左端點屬于(從s可以到達的屬于U的頂點), 我們可以走右端點而走出一條交錯路(增廣路), 就又可以增廣了,顯然不可能。
  • S是最小的點覆蓋。
    單單覆蓋最大匹配的頂點就至少需要|S|個頂點, 再小的話就有點沒有被覆蓋了。
對于連通圖, |最大匹配| + |最小邊覆蓋| = |V|#####
Figrue-B.png
  • 考慮我們要求出一個最小邊覆蓋, 顯然一個邊要盡可能多地覆蓋兩個之前沒有覆蓋過的頂點(紅色)
  • 剩下的邊保證一條邊覆蓋一個定點就好了。(藍色)
  • 證明完畢
|最大獨立集| + |最小頂點覆蓋| = |V|#####

由最大獨立集的定義可知這些頂點兩兩之間沒有邊相連, 那最小頂點覆蓋只需要蓋掉剩下的頂點即可。

代碼##

Dinic#####
struct Edge
{
    int d, cap, rev;
    Edge(int d, int cap, int rev) {
        this->d = d, this->cap = cap, this->rev = rev;
    }
};
 
vector<Edge> G[MAX_V];
int level[MAX_V], iter[MAX_V];
 
void add_edge(int s, int d, int cap) {
    G[s].push_back(Edge(d, cap, G[d].size()));
    G[d].push_back(Edge(s, 0, G[s].size() - 1));
}
 
void build_level(int s) {
    memset(level, -1, sizeof(level));
    queue<int> q;
    level[s] = 0, q.push(s);
    while (!q.empty()) {
        int v = q.front(); q.pop();
        for (int i = 0;i < G[v].size();++i) {
            Edge &e = G[v][i];
            if (e.cap > 0 && level[e.d] < 0) {
                level[e.d] = level[v] + 1;
                q.push(e.d);
            }
        }
    }
}
 
int dfs(int v, int t, int f) {
    if (v == t) return f;
    for (int &i = iter[v];i < G[v].size();++i) {
        Edge &e = G[v][i];
        if (e.cap > 0 && level[v] < level[e.d]) {
            int d = dfs(e.d, t, min(f, e.cap));
            if (d > 0) {
                e.cap -= d, G[e.d][e.rev].cap += d;//把cap換成flow也對,想一想為什么
                return d;
            }
        }
    }
    return 0;
}
 
int max_flow(int s, int t) {
    int flow = 0;
    while (true) {
        build_level(s);
        if (level[t] < 0) return flow;
        memset(iter, 0, sizeof(iter));
        int f = dfs(s, t, INF);
        while (f > 0) flow += f, f = dfs(s, t, INF);
    }
    return flow;
}
Bipartite_matching#####
vector<int> G[MAX_V];
int V, matched[MAX_V];
bool used[MAX_V];

void add_edge(int u, int v) {
    G[u].push_back(v), G[v].push_back(u);
}

int dfs(int v) {
    used[v] = true;
    for (int i = 0;i < G[v].size();++i) {
        int u = G[v][i], w = matched[u];
        if ((w < 0) || (!used[w] && dfs(w))) {
            matched[u] = v, matched[v] = u;
            return true;
        }
    }
    return false;
}

int bipartite_matching() {
    int result = 0;
    memset(matched, -1, sizeof(matched));
    for (int v = 0;v < V;++v) {
        if (matched[v] < 0) {
            memset(used, 0, sizeof(used));
            if (dfs(v)) ++result;
        }
    }
    return result;
}
MinCostMaxFlow#####
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <queue>
#include <utility>
#define MAX_V (55*55*2)
#define MAX_N 55
#define OFFSET 51*50
#define INF 10000000
using namespace std;
typedef long long ll;
namespace MCMF {
    struct Edge
    {
        int d, cap, cost;
        Edge(int d, int cap, int cost)
            :d(d), cap(cap), cost(cost) { }
    };
    vector<Edge> edges;
    vector<int> G[MAX_V];
    inline int rev(int x) { return x ^ 1; }
    void add_edge(int s, int d, int cap, int cost) {
        int k = edges.size();
        edges.push_back(Edge(d, cap, cost));
        edges.push_back(Edge(s, 0, -cost));
        G[s].push_back(k), G[d].push_back(k + 1);
    }
    void init(int n) {
        for (int i = 0;i <= n;++i) G[i].clear();
        edges.clear();
    }
    int dist[MAX_V], pree[MAX_V], inq[MAX_V], prev[MAX_V];
    void spfa(int s) {
        queue<int> q;
        memset(pree, -1, sizeof(pree));
        memset(prev, -1, sizeof(prev));
        memset(dist, 0x3F, sizeof(dist));
        q.push(s), inq[s] = true, dist[s] = 0;
        while (!q.empty()) {
            int v = q.front();
            q.pop(), inq[v] = false;
            for (size_t i = 0;i < G[v].size();++i) {
                int k = G[v][i];
                Edge &e = edges[k];
                if (e.cap > 0 && dist[v] + e.cost < dist[e.d]) {
                    dist[e.d] = dist[v] + e.cost, pree[e.d] = k, prev[e.d] = v;
                    if (!inq[e.d]) q.push(e.d), inq[e.d] = true;
                }
            }
        }
    }
    int argu(int s, int t, int &cnt, ll &f) {
        spfa(s);
        if (prev[t] == -1) return 0;
        int maxf = cnt;
        for (int i = t;i != s;i = prev[i])
            maxf = min(maxf, edges[pree[i]].cap);
        for (int i = t;i != s;i = prev[i]) {
            int k = pree[i];
            edges[k].cap -= maxf, edges[rev(k)].cap += maxf;
            f += edges[k].cost * maxf;
        }
        return maxf;
    }
    ll solve(int s, int t, int cnt) {
        ll flow = 0; int flag = true;
        while (cnt > 0 && flag) {
            flag = argu(s, t, cnt, flow);
            cnt -= flag;
        }
        return !cnt ? flow : -1;
    }
}
int N, K, A[MAX_N][MAX_N];
inline int in(int x,int y) { return x * 50 + y; }
inline int out(int x, int y) { return x * 50 + y + OFFSET; }
int main(int argc,char *argv[]) {
    scanf("%d%d", &N, &K);
    for (int i = 1;i <= N;++i)
        for (int j = 1;j <= N;++j) {
            scanf("%d", &A[i][j]);
            MCMF::add_edge(in(i, j), out(i, j), 1, -A[i][j]);
            MCMF::add_edge(in(i, j), out(i, j), INF, 0);
            if (i != N) add_edge(out(i, j), in(i + 1, j));
            if (j != N) add_edge(out(i, j), in(i, j + 1));
        }
    printf("%d\n", MCMF::solve(out(1, 1), in(N, N), K));
}

引用:
二分圖最大匹配的K?nig定理及其證明
挑戰程序設計競賽(第二版)
[Poj 1459] 網絡流(一) {基本概念與算法}

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容