网 络 最 大 流 网络最大流 网络最大流
题目链接:ybt金牌导航3-2-1 / luogu P3376
题目
如题,给出一个网络图,以及其源点和汇点,求出其网络最大流。
输入
第一行包含四个正整数
N
N
N、
M
M
M、
S
S
S、
T
T
T,分别表示点的个数、有向边的个数、源点序号、汇点序号。
接下来
M
M
M行每行包含三个正整数
u
i
ui
ui、
v
i
vi
vi、
w
i
wi
wi,表示第i条有向边从
u
i
ui
ui出发,到达
v
i
vi
vi,边权为
w
i
wi
wi(即该边最大流量为
w
i
wi
wi)
输出
一行,包含一个正整数,即为该网络的最大流。
样例输入
4 5 4 3
4 2 30
4 3 20
2 3 20
2 1 30
1 3 40
样例输出
50
样例解释
题目中存在
3
3
3条路径:
- 路径为: 4 − − > 2 − − > 3 4-->2-->3 4−−>2−−>3,该路线可通过 20 20 20的流量
- 路径为: 4 − − > 3 4-->3 4−−>3,可通过 20 20 20的流量
- 路径为: 4 − − > 2 − − > 1 − − > 3 4-->2-->1-->3 4−−>2−−>1−−>3,可通过 10 10 10的流量(边 4 − − > 2 4-->2 4−−>2之前已经耗费了 20 20 20的流量)
故流量总计 20 + 20 + 10 = 504 20+20+10=504 20+20+10=504。输出 50 50 50。
数据范围
对于
30
%
30\%
30%的数据:
N
<
=
10
N<=10
N<=10,
M
<
=
25
M<=25
M<=25
对于
70
%
70\%
70%的数据:
N
<
=
200
N<=200
N<=200,
M
<
=
1000
M<=1000
M<=1000
对于
100
%
100\%
100%的数据:
N
<
=
10000
N<=10000
N<=10000,
M
<
=
100000
M<=100000
M<=100000
思路
这是一道非常经典的模板题,我们这里用 D i n i c Dinic Dinic算法来做。
其实就是把 F o r d − F u l k e r s o n Ford-Fulkerson Ford−Fulkerson算法的找增广路径和 d f s dfs dfs增广分开来,并在 d f s dfs dfs时进行了优化。
那优化是怎么样的呢?
我们在
b
f
s
bfs
bfs找增广路径的时候,我们可以将记录这个点是否被走过的
b
o
o
l
bool
bool数组改成
i
n
t
int
int。这样我们除了表示它有没有被走过,还可以表示由源点到这个点走了多远。
接着,我们在
d
f
s
dfs
dfs增广的时候,我们就在某一个点的时候,我们就只找这个值刚好比他大
1
1
1的点走。这样,走出我们的增广路就快了很多了。
(我这里记录的变量名叫
d
i
s
dis
dis)
这样,我们就实现了优化。
代码
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#define rr register
using namespace std;
struct note {
int to, go, op, next;
}e[200001];
int n, m, s, t, le[200001], x, y, z, k, dis[10001], ans;
queue <int> q;
int read() {//快读
int aa = 0;
char c = getchar();
while (c > '9' || c < '0') c = getchar();
while (c <= '9' && c >= '0') {
aa = aa * 10 + c - 48;
c = getchar();
}
return aa;
}
bool bfs() {
while (!q.empty()) q.pop();//清空队列
memset(dis, 0x7f, sizeof(dis));//初始化
dis[s] = 0;//初始化
q.push(s);
while (!q.empty()) {
int now = q.front();
q.pop();
for (rr int i = le[now]; i; i = e[i].next)
if (dis[e[i].to] > dis[now] + 1 && e[i].go) {//没到过且可以灌水
dis[e[i].to] = dis[now] + 1;
if (e[i].to == t) return 1;//到汇点了
q.push(e[i].to);
}
}
return 0;
}
int dfs(int now, int an) {
if (now == t) return an;//到汇点了
int go = 0;
for (rr int i = le[now]; i; i = e[i].next)
if (dis[e[i].to] == dis[now] + 1 && e[i].go) {
int line_go = dfs(e[i].to, min(e[i].go, an - go));//这条增广路最多能流多少
if (!line_go) dis[e[i].to] = -1;//之前找到的不是这条路
e[i].go -= line_go;//流
e[e[i].op].go += line_go;//反向记录
go += line_go;//总流量
if (go == an) break;//已经全部找完了
}
return go;
}
void dinic() {
while (bfs())//还有增广路径
ans += dfs(s, 2147483647);//dfs加最大流
}
int main() {
n = read();//读入
m = read();
s = read();
t = read();
for (rr int i = 1; i <= m; i++) {
x = read();//读入
y = read();
z = read();
e[++k] = (note){y, z, k + 1, le[x]}; le[x] = k;//建邻接表(正向)
e[++k] = (note){x, 0, k - 1, le[y]}; le[y] = k;//建邻接表(反向)
}
dinic();//dinic算法
printf("%d", ans);//输出
return 0;
}