给定一个包含 n n n 个点 m m m 条边的有向图,并给定每条边的容量,边的容量非负。
图中可能存在重边和自环。求从点 S S S 到点 T T T 的最大流。
第一行包含四个整数 n , m , S , T n,m,S,T n,m,S,T。
接下来 m m m 行,每行三个整数 u , v , c u,v,c u,v,c,表示从点 u u u 到点 v v v 存在一条有向边,容量为 c c c。
点的编号从 1 1 1 到 n n n。
输出点 S S S 到点 T T T 的最大流。
如果从点 S S S 无法到达点 T T T 则输出 0 0 0。
2
≤
n
≤
1000
2 \le n \le 1000
2≤n≤1000,
1
≤
m
≤
10000
1 \le m \le 10000
1≤m≤10000,
0
≤
c
≤
10000
0 \le c \le 10000
0≤c≤10000,
S
≠
T
S \neq T
S=T
不断 BFS,用 while
循环不断找残量网络中的增广路径。
每次
跳出循环即得出最大流。
AC Code
C + + \text{C}++ C++
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1010, M = 20010;
const int INF = 1e9;
int n, m, S, T;
int h[N], e[M], f[M], ne[M], idx;
int q[N], d[N], pre[N];
bool st[N];
inline 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(st, false, sizeof st);
int hh = 0, tt = 0;
q[0] = S, d[S] = INF, st[S] = true;
while (hh <= tt)
{
int t = q[hh ++ ];
for (int i = h[t]; ~i; i = ne[i])
{
int j = e[i];
if (!st[j] && f[i])
{
st[j] = true;
d[j] = min(d[t], f[i]);
pre[j] = i;
if (j == T) return true;
q[ ++ tt] = j;
}
}
}
return false;
}
int EK()
{
int flow = 0;
while (bfs())
{
flow += d[T];
for (int i = T; i != S; i = e[pre[i] ^ 1])
f[pre[i]] -= d[T], f[pre[i] ^ 1] += d[T];
}
return flow;
}
int main()
{
int a, b, c;
memset(h, -1, sizeof h);
scanf("%d%d%d%d", &n, &m, &S, &T);
while (m -- )
{
scanf("%d%d%d", &a, &b, &c);
add(a, b, c);
}
printf("%d\n", EK());
return 0;
}
给定一个包含 n n n 个点 m m m 条边的有向图,并给定每条边的容量,边的容量非负。
图中可能存在重边和自环。求从点 S S S 到点 T T T 的最大流。
第一行包含四个整数 n , m , S , T n,m,S,T n,m,S,T。
接下来 m m m 行,每行三个整数 u , v , c u,v,c u,v,c,表示从点 u u u 到点 v v v 存在一条有向边,容量为 c c c。
点的编号从 1 1 1 到 n n n。
输出点 S S S 到点 T T T 的最大流。
如果从点 S S S 无法到达点 T T T 则输出 0 0 0。
2
≤
n
≤
10000
2 \le n \le 10000
2≤n≤10000,
1
≤
m
≤
100000
1 \le m \le 100000
1≤m≤100000,
0
≤
c
≤
10000
0 \le c \le 10000
0≤c≤10000,
S
≠
T
S \neq T
S=T
Dinic 算法其实是 EK 算法的一个暴力的优化,EK 算法每次只能搜索一条增广路径,而 Dinic 算法每次都用 DFS 的形式尽可能多的搜索增广路径。
而图中可能存在环,为了保证 DFS 的过程中不会造成死循环,这里可以使用分层图,这样每次都是一层一层往下搜索,就不会出现死循环。
注意: Dinic 算法对于优化非常敏感,如果优化的不好就可能直接 TLE
AC Code
C + + \text{C}++ C++
#include <iostream>
#include <cstring>
using namespace std;
const int N = 10010, M = 200010;
const int INF = 1e9;
int n, m, S, T;
int h[N], e[M], ne[M], f[M], idx;
int d[N], q[N], cur[N];
bool st[N];
inline 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 = 0;
q[0] = 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 lim)
{
if (u == T) return lim;
int flow = 0;
for (int i = cur[u]; ~i && flow < lim; cur[u] = i, i = ne[i])
{
int j = e[i];
if (d[j] == d[u] + 1 && f[i])
{
int t = find(j, min(f[i], lim - flow));
if (!t) d[j] = -1;
f[i] -= t, f[i ^ 1] += t, flow += t;
}
}
return flow;
}
int dinic()
{
int r = 0, flow = 0;
while (bfs()) while ((flow = find(S, INF))) r += flow;
return r;
}
int main()
{
int a, b, c;
memset(h, -1, sizeof h);
scanf("%d%d%d%d", &n, &m, &S, &T);
while (m -- )
{
scanf("%d%d%d", &a, &b, &c);
add(a, b, c);
}
printf("%d\n", dinic());
return 0;
}
根据 最大流最小割定理,我们知道,最大流与最小割在数值上相等。证明过程见 这里。
因此跑最大流的 Dinic 板子即可,代码没有任何区别,就不再放了。
给定一个包含 n n n 个点 m m m 条边的有向图,并给定每条边的容量和费用,边的容量非负。
图中可能存在重边和自环,保证费用不会存在负环。
求从 S S S 到 T T T 的最大流,以及在流量最大时的最小费用。
第一行包含四个整数 n , m , S , T n,m,S,T n,m,S,T。
接下来 m m m 行,每行三个整数 u , v , c , w u,v,c,w u,v,c,w,表示从点 u u u 到点 v v v 存在一条有向边,容量为 c c c,费用为 w w w。
点的编号从 1 1 1 到 n n n。
输出点 S S S 到点 T T T 的最大流和流量最大时的最小费用。
如果从点
S
S
S 无法到达点
T
T
T 则输出 0 0
。
2
≤
n
≤
5000
2≤n≤5000
2≤n≤5000,
1
≤
m
≤
50000
1≤m≤50000
1≤m≤50000,
0
≤
c
≤
100
0≤c≤100
0≤c≤100,
?
100
≤
w
≤
100
-100 \le w \le 100
?100≤w≤100
S
≠
T
S≠T
S=T
费用流算法本质上是 EK 算法,只不过将找增广路的 BFS 算法替换为了 SPFA 算法。
AC Code
C + + \text{C}++ C++
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 5010, M = 100010, INF = 1e8;
int n, m, S, T;
int h[N], e[M], f[M], w[M], ne[M], idx;
int q[N], d[N], pre[N], incf[N];
bool st[N];
void add(int a, int b, int c, int d)
{
e[idx] = b, f[idx] = c, w[idx] = d, ne[idx] = h[a], h[a] = idx ++ ;
e[idx] = a, f[idx] = 0, w[idx] = -d, ne[idx] = h[b], h[b] = idx ++ ;
}
bool spfa()
{
int hh = 0, tt = 1;
memset(d, 0x3f, sizeof d);
memset(incf, 0, sizeof incf);
q[0] = S, d[S] = 0, incf[S] = INF;
while (hh != tt)
{
int t = q[hh ++ ];
if (hh == N) hh = 0;
st[t] = false;
for (int i = h[t]; ~i; i = ne[i])
{
int ver = e[i];
if (f[i] && d[ver] > d[t] + w[i])
{
d[ver] = d[t] + w[i];
pre[ver] = i;
incf[ver] = min(f[i], incf[t]);
if (!st[ver])
{
q[tt ++ ] = ver;
if (tt == N) tt = 0;
st[ver] = true;
}
}
}
}
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 = e[pre[i] ^ 1])
{
f[pre[i]] -= t;
f[pre[i] ^ 1] += t;
}
}
}
int main()
{
scanf("%d%d%d%d", &n, &m, &S, &T);
memset(h, -1, sizeof h);
while (m -- )
{
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;
}
最后,如果觉得对您有帮助的话,点个赞再走吧!