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

2188. 无源汇上下界可行流 - AcWing题库

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

求一种可行方案使得在所有点满足流量平衡条件的前提下,所有边满足流量限制。

输入格式

第一行包含两个整数 n 和 m。

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

点编号从 1 到 n。

输出格式

如果存在可行方案,则第一行输出 YES,接下来 m 行,每行输出一个整数,其中第 i 行的整数表示输入的第 i 条边的流量。

如果不存在可行方案,直接输出一行 NO

如果可行方案不唯一,则输出任意一种方案即可。

数据范围

1≤n≤200
1≤m≤10200
1≤a,b≤n
0≤c≤d≤10000

输入样例1:
4 6
1 2 1 3
2 3 1 3
3 4 1 3
4 1 1 3
1 3 1 3
4 2 1 3
输出样例1:
YES
1
2
3
2
1
1
输入样例2:
4 6
1 2 1 2
2 3 1 2
3 4 1 2
4 1 1 2
1 3 1 2
4 2 1 2
输出样例2:
NO

解析 :

按照最大流问题的基本解题思路,我们同样需要构建一个流网络。

这个流网络中的可行流需要满足流量守恒和容量限制,其中容量限制不再只有一个上界了,还加了一个下界限制。

假设有一个流网络 G,存在任意一个可行流 f,不存在源点和汇点,我们需要将它转化成一个新的流网络 G′,存在任意一个新的可行流 f′,G′ 中存在源点和汇点,且只有上界没有下界。

对于流网络 G 的可行流 f 中的某一条边 (u,v),容量上界记为 Cu(u,v),容量下界记为 Cl(u,v),当前流量记为 f(u,v)。当前满足 Cl(u,v)≤f(u,v)≤Cu(u,v)。

我们现在需要将 G 转化成 G′,使得可行流 f′ 中任意一条边 (u,v) 满足 0≤f′(u,v)≤C′(u,v)。

这里可以想到一个直观的方法,就是把原等式的每一项都减去 Cl(u,v),那么原等式就变成 0≤f(u,v)−Cl(u,v)≤Cu(u,v)−Cl(u,v)。这就是我们转化的新流网络里可行流的容量限制,且满足只有上界没有下界的要求。

因此转化成新流网络的步骤就是,设置新流网络中边的容量 C′(u,v) 为 Cu(u,v)−Cl(u,v),而 f(u,v)−Cl(u,v) 就是原流网络中的可行流变到新流网络里之后,新可行流中边的流量。根据转化,它是必然满足容量限制的。

虽然新可行流满足容量限制,但是不一定满足流量守恒,因为对于某一个点 x,对于进入点 x 的每条边的流量,都需要减去 Cl(u,v),因此所有进入点 x 的总流量相比原流网络会少 ∑v∈V(v,x),记为 C入,那么新的进入点 x 的总流量就是原总流量 −C入。对于从点 x 出去的每条边的流量,同样需要减去 Cl(u,v),因此所有从点 x 出去的总流量相比原网络会少 ∑v∈V(x,v),记为 C出,那么新的从点 x 出去的总流量就是原总流量 −C出。

对于原流网络,点 x 是一定满足进来的流量等于出去的流量的。但是由于新流网络中每条边都需要减去一个固定的量,假设进入点 x 的边有 a 条,从点 x 出去的边有 b 条,如果 a>b,那么就会导致 C入>C出,就不满足流量守恒了。

怎么解决流量不守恒呢,可以发现现在进入的流量会比出去的流量少 C入−C出,我们只需要把少进入的流量补上,只需要从源点向点 x 连一条容量为 C入−C出 的边。反之如果 C入<C出,反过来需要从点 x 向汇点连一条容量为 C出−C入 的边。这样就满足流量守恒了。

注意这里还有一点,就是补完流量守恒之后,我们必须要保证这些补进来的边必须都是满流,才是流量守恒,因此在原图中的可行流其实对应的是新图中的最大流。

综上,得出原流网络中可行流的集合与新流网络中最大流的集合是一一对应的。那么原流网络中是否存在可行流就等价于新流网络中是否存在最大流,即判断从源点出去的边是否满流。

如果新图中存在最大流,说明原图中存在可行流,我们只需要将新图的最大流通过逆操作转化回原图的可行流就行了,转化方式就是每条边的流量加上 Cl(u,v)。

作者:小小_88
链接:https://www.acwing.com/solution/content/122633/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

//————————————————————————————————————————2024/2/21
#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 + 5, M = (10200 + N) * 2, INF = 0x3f3f3f3f;
int n, m, S, T;
int h[N], e[M], f[M], l[M], ne[M], idx;
int q[N], d[N], cur[N], A[N];

void add(int a, int b, int c, int d) {
	e[idx] = b, f[idx] = d - c, l[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;
	//cout << "______________________" << ret << endl;
	return ret;
}

int main() {
	cin >> n >> m ;
	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, c, d);
		A[a] -= c, A[b] += c;
	}
	int tot = 0;
	for (int i = 1; i <= n; i++) {
		if (A[i] > 0)add(S, i, 0, A[i]), tot += A[i];
		else if (A[i] < 0)add(i, T, 0, -A[i]);
	}
	//cout << "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL   " << tot << endl;
	if (dinic() != tot)cout << "NO" << endl;
	else {
		cout << "YES" << endl;
		for (int i = 0; i < m*2; i+=2) {
			printf("%d\n", f[i ^ 1]+l[i]);
		}
	}
	return 0;
}

//——————————————————————————————————2024/1/04
#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>
using namespace std;
const int N = 2e2 + 5, M = (10200 + N) * 2,INF=1e9;
int n, m,S,T;
int h[N], e[M], f[M], l[M], ne[M], idx;
int q[N], d[N], cur[N], A[N];

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

int 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 ver = e[i];
			if (d[ver] == -1 && f[i]) {
				d[ver] = d[t] + 1;
				cur[ver] = h[ver];
				if (ver == T)return 1;
				q[++tt] = ver;
			}
		}
	}
	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]) {
		cur[u] = i;
		int ver = e[i];
		if (d[ver] == d[u] + 1 && f[i]) {
			int t = find(ver, min(f[i], limit - flow));
			if (!t)d[ver] = -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() {
	scanf("%d%d", &n, &m);
	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, c, d);
		A[a] -= c, A[b] += c;
	}
	int tot = 0;
	for (int i = 1; i <= n; i++) {
		if (A[i] > 0)add(S, i, 0, A[i]), tot += A[i];
		else if (A[i] < 0)add(i, T, 0, -A[i]);
	}
	if (Dinic() != tot)cout << "NO" << endl;
	else {
		cout << "YES" << endl;
		for (int i = 0; i < m * 2; i += 2)
			printf("%d\n", f[i ^ 1] + l[i]);
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值