//poj1273,网络流之最大流主要概念:
//1,源点,即整个图遍历的起点,POJ1273中是1,注意它只流出,不流入
//2.汇点,即终点POJ1273中为读入的下标,注意它只流入不流出。
//3.容量,即从一点到另一点的最大流量,上限值
//4.最大流问题就是知道源点,汇点,每一条边的容量,求从源点到汇点的最大流量
//5.残余网络:针对每条边的流量,就是每一条边还剩余的流量,正常是可以用另一个数组表示用了多少,这里为了节省空间,就将增加的流量减去,比如原先通过100,增加了10,那么还可以增加90
//6.增广路:简单来说就是一条通路,只不过每一条边(注意是这条路径上的!)的流量都比原来的残余网络(原来路径上的)要大。
//这里有几个问题值得关注:
//1,最大流的大小在哪取,简单来说就是从源点流出的总量,或流入汇点的总量,当然这里程序是记录下每一次增加的流量,因为初始状态没有流量,所以每增加一次,必然就多一些流量(因为增广路肯定经过源点的嘛)
//2. 何时取到最大流的图,即不存在增广路,因为如果存在增广路必然还可以增加流量。
//3.注意理解反向边,大致就是假设A路,B路都经过S边,如果我A路走S这条路就将另一种可能堵死了,所以A路要有反向边,B路可以通过反向边行进(因为经过一次反向边相当于把流量又还回去)
//这部分听起来很复杂,但是实现起来很容易,只需记录下该节点前一个是什么,然后找到增广路后倒退,(非常像并查集有木有~~)从前一个节点到后一个节点的通路可用流量减少,反向回路流量增加~~
//流程就是先用邻接矩阵存储,然后每一次找增广路,找的过程中事情较多,要记录下该节点的前一个,返回可增加的流量,寻找可增加流量的过程需要理解,可比作数组寻找最小值,即默认之前一个数组里保存的是可增加的最小值
//然后看后面路径是否小于最小值,如果后面可增长的路径已经小于最小值,那么可增长的最小值要更新,
//若增长路不存在,那么说明已经是最大流的图了,返回最大流,否则开始从尾向头更改路径剩余流量。
//1,源点,即整个图遍历的起点,POJ1273中是1,注意它只流出,不流入
//2.汇点,即终点POJ1273中为读入的下标,注意它只流入不流出。
//3.容量,即从一点到另一点的最大流量,上限值
//4.最大流问题就是知道源点,汇点,每一条边的容量,求从源点到汇点的最大流量
//5.残余网络:针对每条边的流量,就是每一条边还剩余的流量,正常是可以用另一个数组表示用了多少,这里为了节省空间,就将增加的流量减去,比如原先通过100,增加了10,那么还可以增加90
//6.增广路:简单来说就是一条通路,只不过每一条边(注意是这条路径上的!)的流量都比原来的残余网络(原来路径上的)要大。
//这里有几个问题值得关注:
//1,最大流的大小在哪取,简单来说就是从源点流出的总量,或流入汇点的总量,当然这里程序是记录下每一次增加的流量,因为初始状态没有流量,所以每增加一次,必然就多一些流量(因为增广路肯定经过源点的嘛)
//2. 何时取到最大流的图,即不存在增广路,因为如果存在增广路必然还可以增加流量。
//3.注意理解反向边,大致就是假设A路,B路都经过S边,如果我A路走S这条路就将另一种可能堵死了,所以A路要有反向边,B路可以通过反向边行进(因为经过一次反向边相当于把流量又还回去)
//这部分听起来很复杂,但是实现起来很容易,只需记录下该节点前一个是什么,然后找到增广路后倒退,(非常像并查集有木有~~)从前一个节点到后一个节点的通路可用流量减少,反向回路流量增加~~
//流程就是先用邻接矩阵存储,然后每一次找增广路,找的过程中事情较多,要记录下该节点的前一个,返回可增加的流量,寻找可增加流量的过程需要理解,可比作数组寻找最小值,即默认之前一个数组里保存的是可增加的最小值
//然后看后面路径是否小于最小值,如果后面可增长的路径已经小于最小值,那么可增长的最小值要更新,
//若增长路不存在,那么说明已经是最大流的图了,返回最大流,否则开始从尾向头更改路径剩余流量。
#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
int a[201][201];
int end;//终点
int path[201];
int bfs(int s,int e)
{
int i,u,temp;
int flow[201];
int vis[201];
memset(vis,0,sizeof(vis));
memset(flow,0,sizeof(flow));
queue<int>q;
flow[s]=99999;//让起点的流量为最大值,因为接下来是比较从起点到周围的距离,必然是要记录从起点到该点的距离,所以flow[s]要最大
vis[s]=1;//标记起点已经被访问
q.push(s);
while(!q.empty())
{
temp=q.front();
q.pop();
for (i=1;i<=end;i++)
if ((!vis[i]&&a[temp][i]>0))//如果没有被访问过,同时存在回路
{
flow[i]=a[temp][i]<flow[temp]?a[temp][i]:flow[temp];//这就有点像在数组里面找最小值一样,从起点到终点存在回路,并且每一段都存在可增长值,最后找出所有可增值得最小值
vis[i]=1;
path[i]=temp;//记录路径((i的前一个是temo),因为马上还要修改这条路上的剩余流量
q.push(i);
}
}
return flow[e];//如果不存在增广路或者图已经断开,那么返回0,否则返回flow[e],即这次扫描图后得到的可增加的最小值
}
int EK(int s,int e)
{
int max1=0,v,u;
while(v=bfs(s,e))//当流量还可以增加时
{
u=e;//记录下终点,由终点向起点回归,此时不能改变e
while(u!=s)
{
a[path[u]][u]-=v;//从前一个节点到后一个节点的可用流量要减少(相当于实际流通容量增加)
a[u][path[u]]+=v;//同时增加反向回路,反向回路的值增加
u=path[u];//同时往前递归,这让我想起了并查集的内容~~
}
max1+=v;//同时从起点流出的流量加v
}
return max1;
}
int main()
{
int i,j,n,v,x,y;
while(scanf("%d%d",&n,&end)==2)
{
memset(a,0,sizeof(a));
for (i=1;i<=n;i++)
{
scanf("%d%d%d",&x,&y,&v);//从x到y的距离是v
a[x][y]+=v;//这里很坑,可能出现相同的边
}
cout<<EK(1,end)<<endl;
}
return 0;
}