s-t 最小割
最小割问题可以形象地理解为:为了不让水从 s 流向 t,怎么破坏水沟代价最小?被破坏的水沟必然是从 s 到 t 的单向水沟。而在有向图流网络 G = (V,E) 中,割把图分成 S 和 T = V - S 两部分,源点 s ∈ S,汇点 t ∈ T,这称为 s - t 割。
最大流最小割定理:源点 s 和 汇点 t 之间的最小割等于 s 和 t 之间的最大流。
需要注意的是,定理中的最大流是指流量,而最小割是指容量。
全局最小割:把 s-t 最小割问题扩展到全局,有全局最小割问题。![]()
问题一,是否存在一个最小割,其中该边被割?
对于一条边的两个端点 u, v,若该边存在一个最小割中,则在最大流的残留网络中一定不存在一条从 u 到 v 的增广路,否则说明不是最小割。所以我们对最大流的残留网络中的强连通分量进行缩点,缩点后图中剩余的边就只剩下两种边了,一种是残留网络中由 t 到 s 建的的反边,另一种是无法连接 s 和 t 的无用边,而这两种边是很好区分的,第一种边的流量等于它的容量也就是满流,而另一种边是没有流量的也就是零流。
问题二,是否对任何一个最小割,都有该边被割?
在问题一的基础上,把强连通分量缩点后,只用一条就能连接缩点 s 和缩点 t 的边就是对于任何一个最小割,都有该边被割。
// 牛客 (https://ac.nowcoder.com/acm/problem/19887)
#include<bits/stdc++.h>
using namespace std;
const int maxn = 4001;
const int maxm = 60001;
struct edge{
int to, next, cap, flow, f1, f2;
}e[maxm << 1]; int head[maxn], tot;
int n, m, s, t, d[maxn];
int dfn[maxn], low[maxn], cnt;
int scc[maxn], sta[maxn], coc, top;
void addedge(int u, int v, int w) {
e[++tot] = {v, head[u], w, 0, 0, 0}, head[u] = tot;
e[++tot] = {u, head[v], 0, 0, 0, 0}, head[v] = tot;
}
void init() {
memset(head, -1, sizeof head), tot = -1;
}
bool bfs() {
memset(d, 0x7f, sizeof d);
queue<int> q; q.push(t);
d[t] = 0;
while(!q.empty()) {
int x = q.front(); q.pop();
for(int i = head[x]; ~i; i = e[i].next) {
int j = e[i].to;
if (d[j] > d[x] + 1 && e[i^1].cap > e[i^1].flow) {
d[j] = d[x] + 1;
q.push(j);
}
}
}
return d[s] != d[0];
}
int dfs(int x, int flow, int used = 0) {
if (x == t) return flow;
for(int i = head[x]; ~i && flow != used; i = e[i].next) {
int j = e[i].to, f;
if (d[x] - 1 == d[j] && e[i].cap > e[i].flow) {
f = dfs(j, min(flow - used, e[i].cap - e[i].flow));
if (f) e[i].flow += f, e[i^1].flow -= f, used += f;
}
}
if (!used) d[x] = 0;
return used;
}
void DFS(int x) {
dfn[x] = low[x] = ++ cnt;
sta[top++] = x;
for(int i = head[x]; ~i; i = e[i].next) {
int j = e[i].to;
if (e[i].cap > e[i].flow)
if (!dfn[j]) {
DFS(j);
low[x] = min(low[j], low[x]);
}
else if (!scc[j]) {
low[x] = min(dfn[j], low[x]);
}
}
if (dfn[x] == low[x]) {
++ coc;
while(1) {
int y = sta[--top];
scc[y] = coc;
if (y == x) break;
}
}
}
void tarjin(int n) {
coc = top = cnt = 0;
for(int i = 1; i <= n; ++ i) {
scc[i] = low[i] = dfn[i] = 0;
}
for(int i = 1; i <= n; ++ i)
if (!scc[i]) DFS(i);
}
int main() {
scanf("%d%d%d%d", &n, &m, &s, &t);
init();
for(int i = 1; i <= m; ++ i) {
int u, v, w; scanf("%d%d%d", &u, &v, &w);
addedge(u, v, w);
}
while(bfs()) dfs(s, INT_MAX);
tarjin(n);
for(int i = 0; i <= tot; i += 2) {
int u = e[i^1].to, v = e[i].to;
if (e[i].flow && scc[u] != scc[v])
e[i].f1 = 1;
if (scc[u] == scc[s] && scc[v] == scc[t])
e[i].f2 = 1;
}
for(int i = 0; i <= tot; i += 2) {
printf("%d %d\n", e[i].f1, e[i].f2);
}
return 0;
}

京公网安备 11010502036488号