洛谷 P3376 【模板】网络最大流
Description
- 如题,给出一个网络图,以及其源点和汇点,求出其网络最大流。
Input
第一行包含四个正整数N、M、S、T,分别表示点的个数、有向边的个数、源点序号、汇点序号。
接下来M行每行包含三个正整数ui、vi、wi,表示第i条有向边从ui出发,到达vi,边权为wi(即该边最大流量为wi)
Output
- 一行,包含一个正整数,即为该网络的最大流。
Sample Input
4 5 4 3 4 2 30 4 3 20 2 3 20 2 1 30 1 3 40
Sample Output
50
Data Size
对于30%的数据:N<=10,M<=25
对于70%的数据:N<=200,M<=1000
对于100%的数据:N<=10000,M<=100000
题解:
- 最大流。
- 关于网络流-最大流的学习,我翻了N多篇博客,我也不懂我是从哪一篇学会的,所以这里就不再展开个人的算法讲解。
- 只说说我在学网络流中遇到的一个思维障碍——建反向边。
- 为什么要建方向边,网上大多都是说:因为bfs找到的路径不一定最优,所以建反边是为了给它“反悔”的机会。
- 嘛... ...上述那样说的话我的确感性理解了。也就是说,萌新自欺欺人的话就这样记也行。
- 但是我对建反向边的理由有了个自己的理解:
- 现有一图:(红线为反向边
- 显然,肉眼观察,最大流为15。即5人帮从1 -> 2 -> 4,10人帮从1 -> 3 -> 4。
- 假设第一次bfs找到的路径为1 -> 2 -> 3 -> 4。
- 最大流 += 5(5),路径上所有边权 -= 5,反向边 += 5,那么图就变成了:
- 即5人帮从1 -> 2 -> 3 -> 4。
- 第二次bfs找到路径1 -> 3 -> 2 -> 4。
- 最大流 += 5(10),路径上所有边权 -= 5,反向边 += 5,那么图就变成了:
- 这步操作的实际含义是什么呢?即10人帮中派出了5人,他们从1走到3,走到3时,发现曾经有5个人来过这里!他们怎么知道的呢?反向边告诉他们的!于是,他们决定与原5人帮交换灵魂。即原5人帮变成了现在的5个人,现在的5个人变成原5人帮,然后各自继续走对方的道路。
- 那么现在的状况就是:
- 现在的5个人与原5人帮交换灵魂后,继续行走。
- 那其实这种走法不就等价于:
- 5人帮走5人帮的道路,现在的5个人走5个人的道路。
- 第三次bfs找到路径1 -> 3 -> 5
- 最大流 += 5(15),路径上所有边权 -= 5,反向边 += 5,那么图就变成了:
- 即10人帮中剩下的5个人粗发,当他们走到3时,发现没有人可以灵魂交换。于是便继续走,最终走到了4号点
- 第四次bfs发现走不到4号点了,EK算法结束。
- 至此,我们求出了此图最大流为15。
- 通过上面的解释,相信对为什么要建反边有了一定的理解了。因为,为了使“流”有灵魂交换的机会,从而不会遗漏任何一种情况!
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#define N 10005
#define M 200005
#define inf 0x7fffffff
using namespace std;
struct Pre {int u, e;} pre[N];
struct E {int next, to, dis;} e[M];
int n, m, s, t, num = 1;
int h[N];
bool vis[N];
int read()
{
int x = 0; char c = getchar();
while(c < '0' || c > '9') c = getchar();
while(c >= '0' && c <= '9') {x = x * 10 + c - '0'; c = getchar();}
return x;
}
void add(int u, int v, int w)
{
e[++num].next = h[u];
e[num].to = v;
e[num].dis = w;
h[u] = num;
}
bool bfs()
{
memset(vis, 0, sizeof(vis));
pre[s].u = 0;
queue<int> que;
vis[s] = 1, que.push(s);
while(que.size())
{
int now = que.front(); que.pop();
for(int i = h[now]; i != 0; i = e[i].next)
{
int to = e[i].to;
if(!vis[to] && e[i].dis)
{
pre[to].u = now;
pre[to].e = i;
vis[to] = 1;
que.push(to);
if(to == t) return 1;
}
}
}
return 0;
}
int EK()
{
int ans = 0;
while(bfs())
{
int Min = inf;
for(int i = t; i != s; i = pre[i].u)
Min = min(Min, e[pre[i].e].dis);
for(int i = t; i != s; i = pre[i].u)
e[pre[i].e].dis -= Min,
e[pre[i].e ^ 1].dis += Min;
ans += Min;
}
return ans;
}
int main()
{
cin >> n >> m >> s >> t;
for(int i = 1; i <= m; i++)
{
int u = read(), v = read(), w = read();
add(u, v, w), add(v, u, 0);
}
cout << EK();
return 0;
}