2189. 有源汇上下界最大流(最大流,上下界可行流,模板题)

文章详细解释了如何在给定向量图中计算最大流,涉及虚拟源点、流量守恒规则和使用Dinic算法求解的过程。

 活动 - AcWing

给定一个包含 n 个点 m 条边的有向图,每条边都有一个流量下界和流量上界。

给定源点 S 和汇点 T,求源点到汇点的最大流。

输入格式

第一行包含四个整数 n,m,S,T。

接下来 m 行,每行包含四个整数 a,b,c,d 表示点 a 和 b 之间存在一条有向边,该边的流量下界为 c,流量上界为 d。

点编号从 1 到 n。

输出格式

输出一个整数表示最大流。

如果无解,则输出 No Solution

数据范围

1≤n≤202
1≤m≤9999
1≤a,b≤n
0≤c≤d≤105

输入样例:
10 15 9 10
9 1 17 18
9 2 12 13
9 3 11 12
1 5 3 4
1 6 6 7
1 7 7 8
2 5 9 10
2 6 2 3
2 7 0 1
3 5 3 4
3 6 1 2
3 7 6 7
5 10 16 17
6 10 10 11
7 10 14 15
输出样例:
43

解析: 

为什么要建新图?
因为此题有上下界,根据我们上题的经验,需要建虚拟源点(S、T)补流;

但是原图有自带的源汇点(s、t),源汇点不保证流量守恒(其他点都保证),所以需要补一条 t —— s 的满容量边,让原图流量守恒,只有满足 {流量守恒容量上限} 我们才可以这样建图。

还记得上题的结论吗,建出这个新图的意义在于:

在这种状态下建出的新图,可以发现若要新图的流对应原图的可行流,需要保证虚拟源点S的出边都满流;

反过来,只要保证新图的流是满流,我们就可以说这个流是原图的一个可行流。

建了这个新图,在这种满流的状态下我们可以发现一些很特殊的性质:
任意两个新图的满流相减(流量相减),S出、T入 这些边的流量相同会抵消掉,剩下的流量居然是新图中 s —— t 的流量(这里s、t可以换成任意两点,只要不是S、T就行,但我们此题要求的与s、t有关)

我们设新图两个满流为 f1、f2,可以得到公式 |f1−f2|=f(s−>t),移下项,f1=f2+f(s−>t);

一开始说过新图的流对应原图可行流,所以 f1 是原图的一个可行流;

若要 f1 最大,因为 f2 固定了(满流),所以要尽可能让 f(s−>t) 大

要求 max(fs−t) 就在新图上跑个 dinic(s−t) 即可,不过要注意删掉 t —— s 的边,不然流量一直循环求不出来

最后答案 ans=满流f2+max(fs−t)

参考至: AcWing 2189. 有源汇上下界最大流(简易又不空洞的理解) - AcWing

这里补充一点: 满流f2== f[idx - 1],是因为 f[idx - 1] 是原图对应新图中的一个可行流,原图的可行流等于 f[idx - 1] + 此边的容量下届,又因为此边的容量下届==0,所以原图的可行流等于 f[idx - 1]。

#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<math.h>
#include<map>
#include<sstream>
#include<deque>
#include<unordered_map>
#include<unordered_set>
#include<bitset>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
const int N = 2e2 + 10, M = (1e4 + N) * 2, INF = 0x3f3f3f3f;
int n, m, S, T;
int h[N], e[M], f[M], ne[M], idx;
int q[N], d[N], cur[N],A[N];

void add(int a, int b, int c) {
	e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx++;
	e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx++;
}

bool bfs() {
	int hh = 0, tt = 0;
	memset(d, -1, sizeof d);
	q[0] = S, d[S] = 0, cur[S] = h[S];
	while (hh <= tt) {
		int t = q[hh++];
		for (int i = h[t]; i != -1; i = ne[i]) {
			int j = e[i];
			if (d[j] == -1 && f[i]) {
				d[j] = d[t] + 1;
				cur[j] = h[j];
				if (j == T)return 1;
				q[++tt] = j;
			}
		}
	}
	return 0;
}

int find(int u, int limit) {
	if (u == T)return limit;
	int flow=0;
	for (int i = cur[u]; i != -1 && flow < limit; i = ne[i]) {
		int j = e[i];
		cur[u] = i;
		if (d[j] == d[u] + 1 && f[i]) {
			int t = find(j, min(f[i], limit - flow));
			if (!t)d[j] = -1;
			f[i] -= t, f[i ^ 1] += t, flow += t;
		}
	}
	return flow;
}

int dinic() {
	int ret = 0, flow;
	while (bfs())while (flow = find(S, INF)) {
		ret += flow;
	}
	return ret;
}

int main() {
	int s, t;
	cin >> n >> m >> s >> t;
	S = 0, T = n + 1;
	memset(h, -1, sizeof h);
	for (int i = 1, a, b, c, d; i <= m; i++) {
		scanf("%d%d%d%d", &a, &b, &c, &d);
		add(a, b, d - c);
		A[a] -= c, A[b] += c;
	}
	int tot = 0;
	for (int i = 1; i <= n; i++) {
		if (A[i] > 0)add(S, i, A[i]), tot += A[i];
		else if (A[i] < 0)add(i, T, -A[i]);
	}
	add(t, s, INF);
	if (dinic() < tot)cout << "No Solution" << endl;
	else {
		int ret = f[idx - 1];
		S = s, T = t;
		f[idx - 1] = 0, f[idx - 2] = 0;
		printf("%d\n", ret + dinic());
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值