对于一个网络流图
我们的定义割
最小割就是求得一个割
定理:$f(s,t){\max}=c(s,t){\min}$
对于任意一个可行流
如果我们求出了最大流
结合前面的不等式,我们可以知道此时
通过 最大流最小割定理,我们可以直接得到如下代码:
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <queue>
const int N = 1e4 + 5, M = 2e5 + 5;
int n, m, s, t, tot = 1, lnk[N], ter[M], nxt[M], val[M], dep[N], cur[N];
void add(int u, int v, int w) {
ter[++tot] = v, nxt[tot] = lnk[u], lnk[u] = tot, val[tot] = w;
}
void addedge(int u, int v, int w) { add(u, v, w), add(v, u, 0); }
int bfs(int s, int t) {
memset(dep, 0, sizeof(dep));
memcpy(cur, lnk, sizeof(lnk));
std::queue<int> q;
q.push(s), dep[s] = 1;
while (!q.empty()) {
int u = q.front();
q.pop();
for (int i = lnk[u]; i; i = nxt[i]) {
int v = ter[i];
if (val[i] && !dep[v]) q.push(v), dep[v] = dep[u] + 1;
}
}
return dep[t];
}
int dfs(int u, int t, int flow) {
if (u == t) return flow;
int ans = 0;
for (int &i = cur[u]; i && ans < flow; i = nxt[i]) {
int v = ter[i];
if (val[i] && dep[v] == dep[u] + 1) {
int x = dfs(v, t, std::min(val[i], flow - ans));
if (x) val[i] -= x, val[i ^ 1] += x, ans += x;
}
}
if (ans < flow) dep[u] = -1;
return ans;
}
int dinic(int s, int t) {
int ans = 0;
while (bfs(s, t)) {
int x;
while ((x = dfs(s, t, 1 << 30))) ans += x;
}
return ans;
}
int main() {
scanf("%d%d%d%d", &n, &m, &s, &t);
while (m--) {
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
addedge(u, v, w);
}
printf("%d\n", dinic(s, t));
return 0;
}
我们可以通过从源点
void dfs(int u) {
vis[u] = 1;
for (int i = lnk[u]; i; i = nxt[i]) {
int v = ter[i];
if (!vis[v] && val[i]) dfs(v);
}
}
只需要将每条边的容量变为
Warning
这个割边数量并没有保证是在最小割的前提下,所以最下方的例题不能做如此简单的处理。具体解法可以参见题解,不要被这句话误导了。
有
这是一个经典的 二者选其一 的最小割题目。我们对于每个集合设置源点
注意到当源点和汇点不相连时,代表这些点都选择了其中一个集合。如果将连向
最小割就是最小花费。
[!NOTE] AcWing 1399. 控制污染奶
题意: TODO
[!TIP] 思路
有向图网络流 求最小割
详细代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const int N = 40, M = 2010;
const LL INF = 1e18;
int n, m, S, T;
int h[N], e[M], ne[M], idx;
LL f[M];
int q[N], d[N], cur[N];
void add(int a, int b, LL 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() {
memset(d, -1, sizeof d);
int hh = 0, tt = -1;
q[ ++ tt] = S, d[S] = 0, cur[S] = h[S];
while (hh <= tt) {
int t = q[hh ++ ];
for (int i = h[t]; ~i; 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 true;
q[ ++ tt] = j;
}
}
}
return false;
}
LL find(int u, LL limit) {
if (u == T) return limit;
LL flow = 0;
for (int i = cur[u]; ~i && flow < limit; i = ne[i]) {
cur[u] = i;
int j = e[i];
if (d[j] == d[u] + 1 && f[i]) {
LL t = find(j, min(f[i], limit - flow));
if (!t) d[j] = -1;
f[i] -= t, f[i ^ 1] += t, flow += t;
}
}
return flow;
}
LL dinic() {
LL r = 0, flow;
while (bfs()) while (flow = find(S, INF)) r += flow;
return r;
}
void init() {
for (int i = 0; i < idx; i += 2) {
f[i] += f[i ^ 1];
f[i ^ 1] = 0;
}
}
int main() {
cin >> n >> m;
S = 1, T = n;
memset(h, -1, sizeof h);
while (m -- ) {
int a, b, c;
cin >> a >> b >> c;
add(a, b, c * 10000ll + 1);
}
LL res = dinic();
cout << res / 10000 << ' ' << res % 10000 << endl;
for (int i = 0; i < idx; i += 2) {
init();
LL t = f[i];
f[i] = 0;
LL r = dinic();
if (r == res - t) {
cout << i / 2 + 1 << endl;
res = r;
} else f[i] = t;
}
return 0;
}
[!NOTE] AcWing 1409. 奶牛通信
题意: TODO
[!TIP] 思路
拆点 最小割
详细代码
// 最小割 拆点
#include <bits/stdc++.h>
using namespace std;
const int N = 210, M = 2610, INF = 1e8;
int n, m, S, T;
int h[N], e[M], f[M], ne[M], idx;
int q[N], d[N], cur[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() {
memset(d, -1, sizeof d);
int hh = 0, tt = -1;
q[ ++ tt] = S, d[S] = 0, cur[S] = h[S];
while (hh <= tt) {
int t = q[hh ++ ];
for (int i = h[t]; ~i; 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 true;
q[ ++ tt] = j;
}
}
}
return false;
}
int find(int u, int limit) {
if (u == T) return limit;
int flow = 0;
for (int i = cur[u]; ~i && flow < limit; i = ne[i]) {
cur[u] = i;
int j = e[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 r = 0, flow;
while (bfs()) while (flow = find(S, INF)) r += flow;
return r;
}
void init() {
for (int i = 0; i < idx; i += 2) {
f[i] += f[i ^ 1];
f[i ^ 1] = 0;
}
}
int main() {
memset(h, -1, sizeof h);
// 拆点
cin >> n >> m >> S >> T;
S += n;
for (int i = 1; i <= n; ++ i ) add(i, i + n, 1);
while (m -- ) {
int a, b;
cin >> a >> b;
add(a + n, b, INF);
add(b + n, a, INF);
}
int res = dinic();
cout << res << endl;
for (int i = 1; i <= n; ++ i ) {
init();
int j = (i - 1) * 2;
f[j] = 0;
int t = dinic();
if (res == t + 1) {
cout << i << ' ';
-- res;
} else f[j] = 1;
}
return 0;
}