dinic求最小割边集 UVA 10480

时间:2021-10-10 19:24:51

根据最大流最小割原理可知:
在数值上 : 最小割=最大流
这样最小割的数值就可以用dinic跑出来,然后如果要是求最小割边集的话,这样的方法就需要稍加改进。
那么我们需要在最大流之后把残余网络中的点重新分为两类,一类为源点组,一类为汇点组,通过从s开始的dfs找到能够到达的点与源点一组,否则与汇点一组,这样在扫一遍所有的边,判断边的两个点是否分别在这两组,如果是的话,那么这条边就是最小割边集中的一条。
下面以 UVA 10480 为例

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
#define met(a, b) memset(a, b, sizeof(a))
struct EDGE {
    int to, next, vol, tot;
} edges[maxn];
const int inf = 0x3f3f3f3f;
int n, m, cur[maxn], a[maxn], ccnt = 0;
int Head[maxn], edgesNum = 0, S = 1, T = 2, u[maxn], v[maxn], dep[maxn];
void addEdge(int u, int v, int vol);
bool bfs(int s, int t);//分层
int dfs(int u, int flow);
int solve(int s, int t);//dinic算法
void dfs1(int u);//扫一遍残余网络
int main() {
    while(~scanf("%d%d", &n, &m) && n + m) {
        int x, y, w;
        met(a, 0);
        met(Head, -1);
        for(int i = 0; i < m; i++) {
            scanf("%d%d%d", &x, &y, &w);
            addEdge(x, y, w);
            u[i] = x;
            v[i] = y;
        }
        solve(S, T);
    }
    return 0;
}
void addEdge(int u, int v, int vol) {
    edges[edgesNum].to = v;
    edges[edgesNum].vol = vol;
    edges[edgesNum].tot = vol;
    edges[edgesNum].next = Head[u];
    Head[u] = edgesNum++;

    edges[edgesNum].to = u;
    edges[edgesNum].vol = vol;
    edges[edgesNum].tot = vol;
    edges[edgesNum].next = Head[v];
    Head[v] = edgesNum++;
}
bool bfs(int s, int t) {
    met(dep, -1);
    dep[s] = 1;
    queue<int> q;
    q.push(s);
    while(!q.empty()) {
        int u = q.front();
        q.pop();
        for(int e = Head[u]; e != -1; e = edges[e].next) {
            int v = edges[e].to;
            if(dep[v] == -1 && v != s && edges[e].vol > 0) {
                dep[v] = dep[u] + 1;
                q.push(v);
            }
        }
    }
    if(dep[t] == -1) return 0;
    else return 1;
}
int dfs(int u, int flow) {
    if(u == T) return flow;
    for(int &e = cur[u]; ~e; e = edges[e].next) {
        int v = edges[e].to;
        if(dep[v] == dep[u] + 1 && edges[e].vol > 0) {
            int di = dfs(v, min(flow, edges[e].vol));
            if(di > 0) {
                edges[e].vol -= di;
                edges[e ^ 1].vol += di;
                return di;
            }
        } 
    }
    return 0;
}
int solve(int s, int t) {
    int max_flow = 0;
    while(bfs(s, t)) {
        for(int i = 1; i <= n; i++) cur[i] = Head[i];
        while(int di = dfs(s, inf)) {
            max_flow += di;
        }
    }
    //一定要在最大流跑完之后才能执行dfs1(int u)这个函数
    dfs1(s); 
    //扫一遍所有的边集
    for(int i = 0; i < m; i++) {
        //判断起点与终点是否在同一组
        if((!a[u[i]] && a[v[i]]) || (!a[v[i]] && a[u[i]])) {
            printf("%d %d\n", u[i], v[i]);
        }
    }
    puts("");
    return max_flow;
}
void dfs1(int u) {
    a[u] = 1;
    for(int e = Head[u]; e != -1; e = edges[e].next) {
        int v = edges[e].to;
        //只有在残余网络流>0的情况下才能继续dfs
        if(!a[v] && edges[e].vol > 0) {
            dfs1(v);
        }
    }
}