算法

网络流与二分图

2016-12-31  本文已影响39人  MrGopher

定义##

最大流问题#####
割#####
最小割问题#####
匹配#####
边覆盖#####
独立集#####
顶点覆盖#####

性质##

最大流 = 最小割#####
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
对于连通图, |最大匹配| + |最小边覆盖| = |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] 网络流(一) {基本概念与算法}

上一篇 下一篇

猜你喜欢

热点阅读