活动 - 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;
}