多源汇最大流问题详解


一、多源汇最大流

1.1问题描述

给定一个包含n个点m条边的有向图,并给定每条边的容量,边的容量非负。其中有Sc个源点,Tc个汇点。图中可能存在重边和自环。求整个网络的最大流。

1.2思路分析

图论学习到网络流阶段,对于这个问题应该不难想到是建立虚拟源汇点,连边,求最大流即可。

在这里插入图片描述

正确性证明:

对于原图可行流,叠加到新图上,对于除ST外的点和边自然满足可行流的限制,对于S,T的相关边由于容量都是正无穷,所以自然也满足可行流的限制

对于新图的可行流,对于除ST外的点都满足流量守恒,新图所有边都满足容量限制,那么撤去S、T和相关边后自然得到了原图的可行流。

所以原图可行流和新图可行流是双射关系,那么新图最大流自然是原图最大流

1.3算法实现

  • 建立虚拟源点S,虚拟汇点T,S和所有源点si建立容量为正无穷的边,所有汇点ti和T建立容量为正无穷的边
  • 跑dinic算法

1.4OJ练习

多源汇最大流 POJ1459 – Power Network (poj.org)

#include <iostream>
#include <queue>
#include <cstring>
#include <algorithm>

using namespace std;
#define int long long
#define sc scanf

const int N = 105, M = (10010 + N * 2) << 1, inf = 1e9;
int head[N], idx, cur[N], d[N], s, t, ns, nt, n, m;

struct edge
{
    int v, c, nxt;
} edges[M];
void addedge(int u, int v, int c)
{
    edges[idx].v = v, edges[idx].c = c, edges[idx].nxt = head[u],
    head[u] = idx++;
}
void add(int u, int v, int c)
{
    addedge(u, v, c), addedge(v, u, 0);
}

int dfs(int u, int limit)
{
    if (u == t)
        return limit;
    int res = 0;
    for (int i = cur[u]; ~i && limit; i = edges[i].nxt)
    {
        cur[u] = i;
        int v = edges[i].v;
        if (d[v] == d[u] + 1 && edges[i].c)
        {
            int incf = dfs(v, min(edges[i].c, limit));
            if (!incf)
                d[v] = 0;
            res += incf, edges[i].c -= incf, edges[i ^ 1].c += incf, limit -= incf;
        }
    }
    return res;
}

bool bfs()
{
    memset(d, 0, sizeof(d));
    queue<int> q;
    q.push(s), d[s] = 1;
    while (q.size())
    {
        int u = q.front();
        q.pop();
        for (int i = head[u]; ~i; i = edges[i].nxt)
        {
            int v = edges[i].v;
            if (!d[v] && edges[i].c)
            {
                d[v] = d[u] + 1, q.push(v);
                if (v == t)
                    return true;
            }
        }
    }
    return false;
}

int dinic()
{
    int res = 0;
    while (bfs())
        memcpy(cur, head, sizeof(head)), res += dfs(s, inf);
    return res;
}

void solve()
{
    while (~sc("%lld%lld%lld%lld", &n, &ns, &nt, &m))
    {
        memset(head, -1, sizeof(head)), idx = 0, s = 0, t = n + 1;
        for (int i = 0, a, b, c; i < m; i++)
        {
            while (getchar() != '(')
                ;
            sc("%lld,%lld)%lld", &a, &b, &c), add(a + 1, b + 1, c);
        }
        for (int i = 0, a, b; i < ns; i++)
        {
            while (getchar() != '(')
                ;
            sc("%lld)%lld", &a, &b), add(s, a + 1, b);
        }
        for (int i = 0, a, b; i < nt; i++)
        {
            while (getchar() != '(')
                ;
            sc("%lld)%lld", &a, &b), add(a + 1, t, b);
        }
        printf("%lld\n", dinic());
    }
}
signed main()
{
    // ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    // freopen("in.txt", "r", stdin);
    int _ = 1;
    // cin >> _;
    while (_--)
        solve();
    return 0;
}
  • 10
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

EQUINOX1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值