最小割

最小割定义


  1. : 点的划分方式, 将图中的所有点划分成2个集合,源点s,汇点t, s ∈ S , t ∈ T s\in S,t \in T sS,tT.
  2. 割的容量表示所有从ST的边的容量之和, c ( S , T ) = Σ u ∈ S , v ∈ T c ( u , v ) c(S,T) = \Sigma_{u \in S,v \in T}c(u,v) c(S,T)=ΣuS,vTc(u,v)
  3. 最小割问题: 求一个割的方法使得割的容量最小

最大流 = 最小割

  • 求最小割和最大流可以看做是一个问题

最小割例题 luogu P1344

问题
  1. 最小割的值
  2. 最小割边的个数

直接求最小割

  • 第一问直接求最大流
  • 对于第二问重新建图边权改为1,那么最大流=最小割=最小割的边数
#include <bits/stdc++.h>
using namespace std;
using ll = long long;

const int maxn = 50;
const int maxm = 1e4+5;
const ll inf = 1e9+1;

struct Edge{
    int v,next;
    ll c;
} edge[maxm];

int n,m,tot,source,sink;
int head[maxn],dep[maxn];

void add(int u,int v,ll c) {
    edge[tot].v=v; edge[tot].c=c;
    edge[tot].next=head[u]; head[u]=tot++;
}

void addedge(int u,int v,ll c) {
    add(u,v,c);
    add(v,u,0);
}

bool bfs() {
    queue<int> que;
    memset(dep,-1,sizeof dep);
    dep[source] = 0; // 深度标记数组
    que.push(source);
    while (!que.empty()) {
        int cur = que.front(); que.pop();
        for (int i=head[cur]; i!=-1; i=edge[i].next) {
            int to = edge[i].v;
            if (edge[i].c>0 && dep[to]==-1) {
                dep[to] = dep[cur] + 1;
                que.push(to);
            }
        }
    }
    return dep[sink] != -1;
}

ll dfs(int u,ll delta) {
    if (u == sink)
        return delta;
    ll flow = 0;
    for (int i=head[u]; i!=-1; i=edge[i].next) {
        int to = edge[i].v;
        if (edge[i].c>0 && dep[to]==dep[u]+1) {
            ll temp = dfs(to,min(delta-flow,edge[i].c));
            edge[i].c -= temp;
            edge[i^1].c += temp;
            flow += temp;
        }
    }
    if (!flow) dep[u] = -1;
    return flow;
}

ll Dinic() {
    ll ans = 0;
    while(bfs()) {
        while (1) {
            ll temp = dfs(source,inf);
            if (!temp) break;
            ans += temp;
        }
    }
    return ans;
}

struct Storage {
    int u,v;
} storage[maxm];


int main() {
    memset(head,-1,sizeof head);
    scanf("%d%d",&n,&m);
    source = 1;
    sink = n;
    for (int i=1; i<=m; ++i) {
        int u,v;
        ll c;
        scanf("%d %d %lld",&u,&v,&c);
        addedge(u,v,c);
        storage[i].u = u;
        storage[i].v = v;
    }
    ll maxflow = Dinic();
    // 重新建图求 最小割的数量

    memset(head,-1,sizeof head);
    memset(edge,0,sizeof edge);
    tot = 0;

    for (int i=1; i<=m; ++i)
        addedge(storage[i].u,storage[i].v,1);

    ll cut_count = Dinic();

    cout << maxflow << " " << cut_count << endl;
    return 0;
}

最小割转最大流

  1. 建图时边权 w = k ∗ w + 1 w=k*w+1 w=kw+1, S S S为最小割边集 w i ∈ S w_i \in S wiS

w 1 + w 2 + w 3 + . . . + w n = a n s w_1+w_2+w_3+...+w_n = ans w1+w2+w3+...+wn=ans

w 1 ∗ k + w 2 ∗ k + w 3 ∗ k + . . . + w n ∗ k = a n s ∗ k w_1*k+w_2*k+w_3*k+...+w_n*k = ans*k w1k+w2k+w3k+...+wnk=ansk

( w 1 ∗ k + 1 ) + ( w 2 ∗ k + 1 ) + ( w 3 ∗ k + 1 ) + . . . + ( w n ∗ k + 1 ) = a n s ∗ k + n = a n s 1 (w_1*k+1)+(w_2*k+1)+(w_3*k+1)+...+(w_n*k+1) = ans*k+n = ans_1 (w1k+1)+(w2k+1)+(w3k+1)+...+(wnk+1)=ansk+n=ans1

  • 最小割: a n s = a n s 1 / k ans = ans_1/k ans=ans1/k
  • 最大流: c u t = a n s 1 % k cut=ans_1 \% k cut=ans1%k

  1. 注意long long
#include <bits/stdc++.h>
using namespace std;
using ll = long long;

const int maxn = 50;
const int maxm = 1e4+5;
const ll mod = 2000;
const ll inf = 1e9+1;

struct Edge {
    int v,next;
    ll c;
}edge[maxm];

int head[maxn],dep[maxn];
int n,m,tot,source,sink;

void add(int u,int v,ll c){
    edge[tot].v=v; edge[tot].c=c;
    edge[tot].next=head[u]; head[u]=tot++;
}
void addedge(int u,int v,ll c) {
    add(u,v,c);
    add(v,u,0);
}

bool bfs() {
    queue<int> que;
    memset(dep,-1,sizeof dep);
    dep[source] = 0;
    que.push(source);
    while(!que.empty()) {
        int cur = que.front(); que.pop();
        for (int i=head[cur]; i!=-1; i=edge[i].next) {
            int to = edge[i].v;
            if (edge[i].c>0 && dep[to] == -1) {
                dep[to] = dep[cur] + 1;
                que.push(to);
            }
        }
    }
    return dep[sink] != -1;
}

ll dfs(int u,int delta) {
    if (u == sink)
        return delta;
    ll flow = 0;
    for (int i=head[u]; i!=-1; i=edge[i].next) {
        int to = edge[i].v;
        if (edge[i].c>0 && dep[to]==dep[u]+1) {
            ll temp = dfs(to,min(delta-flow,edge[i].c));
            edge[i].c -= temp;
            edge[i^1].c += temp;
            flow += temp;
        }
    }
    if (!flow) dep[u] = -1;
    return flow;
}

ll Dinic() {
    ll ans=0;
    while (bfs()) {
        while(1) {
            ll temp = dfs(source,inf);
            if (!temp) break;
            ans += temp;
        }
    }
    return ans;
}

int main() {
    memset(head,-1,sizeof head);
    scanf("%d%d",&n,&m);
    for (int i=1; i<=m; ++i) {
        int u,v;
        ll c;
        scanf("%d%d%lld",&u,&v,&c);
        addedge(u,v,c*mod+1);
    }
    source = 1;
    sink = n;
    ll ans = Dinic();
    cout << ans/mod << " " << ans%mod << endl;
    return 0;
}
  • 2019/11/18 0:36:23 真晚
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值