#include<iostream>
#include<cstdio>
#include<cmath>
#include<queue>
#include<cstring>
#include<algorithm>
#define lson x<<1
#define rson x<<1|1
#define ll long long
#define rint register int
#define mp(x,y) make_pair(x,y)
using namespace std;
template<typename xxx>void read(xxx &x)
{
x=0;int f=1;char c=getchar();
for(;c<'0'||c>'9';c=getchar()) if(c=='-') f=-1;
for(;c>='0'&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
x*=f;
}
template<typename xxx>void print(xxx x)
{
if(x<0){putchar('-');x=-x;}
if(x>9) print(x/10);
putchar(x%10+'0');
}
const int maxn=200002;
const int inf=0x7fffffff;
int n,m,s,t,maxflow;
struct edge{
int to,last,val;
}e[maxn];
int head[maxn],tot;
inline void add(int from,int to,int val)
{
tot++;
e[tot].to=to;
e[tot].val=val;
e[tot].last=head[from];
head[from]=tot;
}
queue<int>q;
int vis[maxn];
int rem[maxn];//剩余流量
int pre[maxn];
inline int bfs()//用来寻找s,t的最短路并记录,如果s,t不连通则返回0
{
while(q.size()) q.pop();
memset(vis,0,sizeof(vis));//当次增广路不能走重点,但每次增广路互不影响
q.push(s);vis[s]=1;
rem[s]=inf;
while(q.size())
{
int x=q.front();q.pop();
for(rint i=head[x];i;i=e[i].last)
{
if(e[i].val)//忽略流量为零
{
int y=e[i].to;
if(vis[y]) continue;//保证去了的点不在去,也保证下两句正确性
rem[y]=min(rem[x],e[i].val);//到达y时最小剩余流
pre[y]=i;//连向y的边的编号
q.push(y),vis[y]=1;
if(y==t) return 1;//连通肯定能找到t
}
}
}
return 0;
}
inline void update()//更新最短路与反向边优化
{
int x=t;
while(x!=s)
{
int i=pre[x];
e[i].val-=rem[t];
e[i^1].val+=rem[t];
x=e[i^1].to;
}
maxflow+=rem[t];
}
int main()
{
tot=1;//方便正反边转换
read(n);read(m);read(s);read(t);
for(rint i=1;i<=m;i++)
{
int a,b,c;
read(a);read(b);read(c);
add(a,b,c);add(b,a,0);
}
while(bfs())
update();
print(maxflow);
}
/*
网络流学习
1,通过流量不超过容量
2,除源点汇点外其余点流入流出量相同
3,每条边正反方向流量大小相同方向相反
4,剩余流量为容量减去当前流量
5,最大流的值等于最小割的容量
最小割:就是给你一个图,然后给你边权,问你如何在割掉的边权尽量小的情况下把指定的两个点or两个集合分开
选出一些管道,切断以后,图不连通,这些管道的集合就叫割。这些边的容量之和叫做这个割的容量。
推论 任意一个流都小于等于任意一个割
考虑FF算法时,残量网络上没有了增广路。
那么我们假设这时候,从源点经过残量网络能到达的点组成的集合为X,不能到达的点为Y。显然汇点在Y里,并且残量网络上没有从X到Y的边。
可以发现以下事实成立:
1.Y到X的边的流量为0.如果不为0,那么一定存在一条从X到Y的反向边,于是矛盾。
2.X到Y的边流量等于其容量。只有这样它才不会在残量网络中出现。
■根据第一个条件得知:没有流量从X到Y后又回到X。所以当前流量应该等于从X到Y的边的流量之和,而根据第二个条件他又等于X到Y的边容量之和。
■而所有从X到Y的边又构成了一个割,其容量等于这些边的容量之和。
★这意味着我们找到一个割和一个流,使得前者的流量等于后者的容量。而根据前边的结论,最大流的流量不超过这个割的容量,所以这个流一定是最大流。
■同样的,最小割的容量也不会小于这个流的流量,所以这个割也一定是最小割。
■而这也正是FF方法的最后局面,由此我们对出结论:FF是正确的,并且最小割等于最大流
6,最大流等于二分图最大匹配数
7,残量网络=容量网络-流量网络
8,增广路EK算法(FF思想) 十万以内
若一条s到t的路径上所有边都有剩余流量,则该路还可以增加新流量,EK是使用bfs不断找增广路直到没有
每次bfs一条s到t的路径,计算路径上最小值,加入总流量中
将增广路上所有边的残量减去v,反向边的残量加上v。
建反向边,留下一个标记,让后面的流有机会让前面的流走另一条路,给程序有反悔的机会,这样再次从反向边流过就相当于抵消了。。
每条边代表着一个里面流动着不可压缩流体的具有流速限制的管道, 那么反向增广就有点像是反向加压来尝试把流体压回原来的点, 总的效果就是让这条边里的流速减缓或反向, 而让流返回原来的点重新决策.
n*m*m(n为点,m为边),边少时用
*/