网络流模板
EK求最大流
\(O(nm^2)\), 一般可以处理\(1000\)~\(10000\)的网络
每次增广一条路径
#include <bits/stdc++.h>
using namespace std;
const int N = 1e3 + 10;
const int M = 2e4 + 10;
const int INF = 1e9;
int n, m, S, T;
struct edge
{
int to, nxt, flow;
}line[M];
int fist[N], idx;
int d[N], pre[N];
bool st[N];
void add(int x, int y, int z)
{
line[idx] = {y, fist[x], z};
fist[x] = idx ++;
line[idx] = {x, fist[y], 0};
fist[y] = idx ++;
}
bool bfs()
{
queue<int> q;
memset(st, 0, sizeof st);
q.push(S), st[S] = 1, d[S] = INF;
while(!q.empty())
{
int u = q.front(); q.pop();
for(int i = fist[u]; i != -1; i = line[i].nxt)
{
int v = line[i].to;
if(!st[v] && line[i].flow)
{
st[v] = 1;
d[v] = min(line[i].flow, d[u]);
pre[v] = i;
if(v == T) return 1;
q.push(v);
}
}
}
return 0;
}
int EK()
{
int res = 0;
while(bfs())
{
res += d[T];
for(int i = T; i != S; i = line[pre[i] ^ 1].to)
line[pre[i]].flow -= d[T],
line[pre[i] ^ 1].flow += d[T];
}
return res;
}
int main()
{
scanf("%d%d%d%d", &n, &m, &S, &T);
memset(fist, -1, sizeof fist);
for(int i = 1; i <= m; ++ i)
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c);
}
printf("%d\n", EK());
return 0;
}
Dinic求最大流/最小割
\(O(n^2m)\), 一般可以处理\(10000\)~\(100000\)的网络
每次尽可能多的增广路径
防止在环上陷入死循环,使用分层图,处理每个点和起点间的距离
#include <bits/stdc++.h>
using namespace std;
const int N = 1e4 + 10;
const int M = 2e5 + 10;
const int INF = 1e9;
int n, m, S, T;
struct Edge
{
int to, nxt, flow;
}line[M];
int fist[N], idx;
int d[N], cur[N];
void add(int x, int y, int z)
{
line[idx] = {y, fist[x], z};
fist[x] = idx ++;
line[idx] = {x, fist[y], 0};
fist[y] = idx ++;
}
bool bfs()
{
queue<int> q;
memset(d, -1, sizeof d);
q.push(S), d[S] = 0, cur[S] = fist[S];
while(!q.empty())
{
int u = q.front(); q.pop();
for(int i = fist[u]; i != -1; i = line[i].nxt)
{
int v = line[i].to;
if(d[v] == -1 && line[i].flow)
{
d[v] = d[u] + 1;
cur[v] = fist[v];
if(v == T) return 1;
q.push(v);
}
}
}
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 = line[i].nxt)
{
cur[u] = i;
int v = line[i].to;
if(d[v] == d[u] + 1 && line[i].flow)
{
int t = find(v, min(line[i].flow, limit - flow));
if(!t) d[v] = -1;
line[i].flow -= t;
line[i ^ 1].flow += t;
flow += t;
}
}
return flow;
}
int dinic()
{
int res = 0, flow;
while(bfs()) while(flow = find(S, INF)) res += flow;
return res;
}
int main()
{
scanf("%d%d%d%d", &n, &m, &S, &T);
memset(fist, -1, sizeof fist);
for(int i = 1; i <= m; ++ i)
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c);
}
printf("%d\n", dinic());
return 0;
}
无源汇上下界可行流
每条边的容量变成容量上界-容量下界
令 \(c_1\) 为该点少流入的流量,\(c_2\) 为该点少流出的流量
若 \(c_1\) > \(c_2\) ,从源点向该点连一条容量为 \(c_1 - c_2\)的边
若 \(c_1\) < \(c_2\) ,从该点向汇点连一条容量为 \(c_2 - c_1\)的边
#include <bits/stdc++.h>
using namespace std;
const int N = 200 + 10;
const int M = (10200 + N) * 2;
const int INF = 1e9;
int n, m, S, T;
struct Edge
{
int to, nxt, flow;
}line[M];
int fist[N], idx;
int cur[N], d[N];
int A[N], l[M];
void add(int x, int y, int lz, int uz)
{
line[idx] = {y, fist[x], uz - lz};
l[idx] = lz;
fist[x] = idx ++;
line[idx] = {x, fist[y], 0};
fist[y] = idx ++;
}
bool bfs()
{
queue<int> q;
memset(d, -1, sizeof d);
q.push(S), d[S] = 0, cur[S] = fist[S];
while(!q.empty())
{
int u = q.front(); q.pop();
for(int i = fist[u]; i != -1; i = line[i].nxt)
{
int v = line[i].to;
if(d[v] == -1 && line[i].flow)
{
d[v] = d[u] + 1;
cur[v] = fist[v];
if(v == T) return 1;
q.push(v);
}
}
}
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 = line[i].nxt)
{
cur[u] = i;
int v = line[i].to;
if(d[v] == d[u] + 1 && line[i].flow)
{
int t = find(v, min(line[i].flow, limit - flow));
if(!t) d[v] = -1;
line[i].flow -= t;
line[i ^ 1].flow += t;
flow += t;
}
}
return flow;
}
int dinic()
{
int res = 0, flow;
while(bfs()) while(flow = find(S, INF)) res += flow;
return res;
}
int main()
{
scanf("%d%d", &n, &m);
memset(fist, -1, sizeof fist);
S = 0, T = n + 1;
for(int i = 1; i <= m; ++ i)
{
int a, b, c, d;
scanf("%d%d%d%d", &a, &b, &c, &d);
add(a, b, c, d);
A[b] += c, A[a] -= 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) puts("NO");
else
{
puts("YES");
for(int i = 0; i < m * 2; i += 2)
printf("%d\n", line[i ^ 1].flow + l[i]);
}
return 0;
}
有源汇上下界最大流
从汇点向源点连一条容量为 \(+\infty\) 的边,使得所有点流量守恒
按照无源汇上下界可行流的求法建立虚拟源点和汇点,求新图的最大流,判断是否满流
在残留网络上删去汇点到源点容量为 \(+\infty\) 的边, 跑一遍从源点到汇点的最大流
答案为原网络的可行流加上该最大流
#include <bits/stdc++.h>
using namespace std;
const int N = 200 + 10;
const int M = (10000 + N) * 2;
const int INF = 1e9;
int n, m, S, T;
struct Edge
{
int to, nxt, flow;
}line[M];
int fist[N], idx;
int d[N], cur[N], A[N];
void add(int x, int y, int z)
{
line[idx] = {y, fist[x], z};
fist[x] = idx ++;
line[idx] = {x, fist[y], 0};
fist[y] = idx ++;
}
bool bfs()
{
queue<int> q;
memset(d, -1, sizeof d);
q.push(S), d[S] = 0, cur[S] = fist[S];
while(!q.empty())
{
int u = q.front(); q.pop();
for(int i = fist[u]; i != -1; i = line[i].nxt)
{
int v = line[i].to;
if(d[v] == -1 && line[i].flow)
{
d[v] = d[u] + 1;
cur[v] = fist[v];
if(v == T) return 1;
q.push(v);
}
}
}
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 = line[i].nxt)
{
cur[u] = i;
int v = line[i].to;
if(d[v] == d[u] + 1 && line[i].flow)
{
int t = find(v, min(line[i].flow, limit - flow));
if(!t) d[v] = -1;
line[i].flow -= t;
line[i ^ 1].flow += t;
flow += t;
}
}
return flow;
}
int dinic()
{
int res = 0, flow;
while(bfs()) while(flow = find(S, INF)) res += flow;
return res;
}
int main()
{
int s, t;
scanf("%d%d%d%d", &n, &m, &s, &t);
memset(fist, -1, sizeof fist);
S = 0, T = n + 1;
for(int i = 1; i <= m; ++ i)
{
int a, b, c, d;
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) puts("No Solution");
else
{
int res = line[idx - 1].flow;
S = s, T = t;
line[idx - 1].flow = line[idx - 2].flow = 0;
printf("%d\n", res + dinic());
}
return 0;
}
有源汇上下界最小流
和有源汇上下界最大流做法类似,最后在残留网络上跑一遍汇点到源点的最大流即可
答案为原网络可行流减去该最大流
#include <bits/stdc++.h>
using namespace std;
const int N = 50010;
const int M = (N + 125003) * 2;
const int INF = 0x3f3f3f3f;
int n, m, S, T;
struct Edge
{
int to, nxt, flow;
}line[M];
int fist[N], idx;
int cur[N], d[N];
int A[N];
void add(int x, int y, int z)
{
line[idx] = {y, fist[x], z};
fist[x] = idx ++;
line[idx] = {x, fist[y], 0};
fist[y] = idx ++;
}
bool bfs()
{
queue<int> q;
memset(d, -1, sizeof d);
q.push(S), d[S] = 0, cur[S] = fist[S];
while(!q.empty())
{
int u = q.front(); q.pop();
for(int i = fist[u]; i != -1; i = line[i].nxt)
{
int v = line[i].to;
if(d[v] == -1 && line[i].flow)
{
d[v] = d[u] + 1;
cur[v] = fist[v];
if(v == T) return 1;
q.push(v);
}
}
}
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 = line[i].nxt)
{
cur[u] = i;
int v = line[i].to;
if(d[v] == d[u] + 1 && line[i].flow)
{
int t = find(v, min(line[i].flow, limit - flow));
if(!t) d[v] = -1;
line[i].flow -= t;
line[i ^ 1].flow += t;
flow += t;
}
}
return flow;
}
int dinic()
{
int res = 0, flow;
while(bfs()) while(flow = find(S, INF)) res += flow;
return res;
}
int main()
{
int s, t;
scanf("%d%d%d%d", &n, &m, &s, &t);
S = 0, T = n + 1;
memset(fist, -1, sizeof fist);
for(int i = 1; i <= m; ++ i)
{
int a, b, c, d;
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) puts("No Solution");
else
{
int res = line[idx - 1].flow;
S = t, T = s;
line[idx - 1].flow = line[idx - 2].flow = 0;
printf("%d\n", res - dinic());
}
return 0;
}
多源汇最大流
建立一个超级源点,从超级源点向所有源点连一条容量为 \(+\infty\) 的边
建立一个超级汇点,从所有汇点向超级汇点连一条容量为 \(+\infty\) 的边
#include <bits/stdc++.h>
using namespace std;
const int N = 10010;
const int M = (100000 + N) * 2;
const int INF = 1e9;
int n, m, S, T;
struct Edge
{
int to, nxt, flow;
}line[M];
int fist[N], idx;
int cur[N], d[N];
void add(int x, int y, int z)
{
line[idx] = {y, fist[x], z};
fist[x] = idx ++;
line[idx] = {x, fist[y], 0};
fist[y] = idx ++;
}
bool bfs()
{
queue<int> q;
memset(d, -1, sizeof d);
q.push(S), d[S] = 0, cur[S] = fist[S];
while(!q.empty())
{
int u = q.front(); q.pop();
for(int i = fist[u]; i != -1; i = line[i].nxt)
{
int v = line[i].to;
if(d[v] == -1 && line[i].flow)
{
d[v] = d[u] + 1;
cur[v] = fist[v];
if(v == T) return 1;
q.push(v);
}
}
}
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 = line[i].nxt)
{
cur[u] = i;
int v = line[i].to;
if(d[v] == d[u] + 1 && line[i].flow)
{
int t = find(v, min(line[i].flow, limit - flow));
if(!t) d[v] = -1;
line[i].flow -= t;
line[i ^ 1].flow += t;
flow += t;
}
}
return flow;
}
int dinic()
{
int res = 0, flow;
while(bfs()) while(flow = find(S, INF)) res += flow;
return res;
}
int main()
{
int sc, tc;
scanf("%d%d%d%d", &n, &m, &sc, &tc);
S = 0, T = n + 1;
memset(fist, -1, sizeof fist);
for(int i = 1; i <= sc; ++ i)
{
int x;
scanf("%d", &x);
add(S, x, INF);
}
for(int i = 1; i <= tc; ++ i)
{
int x;
scanf("%d", &x);
add(x, T, INF);
}
for(int i = 1; i <= m; ++ i)
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c);
}
printf("%d\n", dinic());
return 0;
}
最小费用最大流
#include <bits/stdc++.h>
using namespace std;
const int N = 5000 + 10;
const int M = (50000 + 10) * 2;
const int INF = 1e9;
int n, m, S, T;
struct Edge
{
int to, nxt, flow, w;
}line[M];
int fist[N], idx;
int pre[N], incf[N], d[N];
bool st[N];
void add(int x, int y, int z, int w)
{
line[idx] = {y, fist[x], z, w};
fist[x] = idx ++;
line[idx] = {x, fist[y], 0, -w};
fist[y] = idx ++;
}
bool spfa()
{
queue<int> q;
memset(d, 0x3f, sizeof d);
memset(incf, 0, sizeof incf);
q.push(S), d[S] = 0, incf[S] = INF;
while(!q.empty())
{
int u = q.front(); q.pop();
st[u] = 0;
for(int i = fist[u]; i != -1; i = line[i].nxt)
{
int v = line[i].to;
if(line[i].flow && d[v] > d[u] + line[i].w)
{
d[v] = d[u] + line[i].w;
pre[v] = i;
incf[v] = min(incf[u], line[i].flow);
if(!st[v])
{
q.push(v);
st[v] = 1;
}
}
}
}
return incf[T] > 0;
}
void EK(int& flow, int& cost)
{
flow = cost = 0;
while(spfa())
{
int t = incf[T];
flow += t, cost += t * d[T];
for(int i = T; i != S; i = line[pre[i] ^ 1].to)
{
line[pre[i]].flow -= t;
line[pre[i] ^ 1].flow += t;
}
}
}
int main()
{
scanf("%d%d%d%d", &n, &m, &S, &T);
memset(fist, -1, sizeof fist);
for(int i = 1; i <= m; ++ i)
{
int a, b, c, d;
scanf("%d%d%d%d", &a, &b, &c, &d);
add(a, b, c, d);
}
int flow, cost;
EK(flow, cost);
printf("%d %d\n", flow, cost);
return 0;
}