1.首先我们思考一个生活中十分常见的场景:自来水的供应。当我们打开水龙头之后,自来水就会哗哗的的流出来,这些水经历了很多个水管来到了家里。我们通过这个模型解释问题。

2.网络流,图论中把水流抽象出来,在有向图中的流统称为网络流,可以代表很多的含义,可以是水,车流量,电流量等等。首先,我们需要知道两个概念:源点和汇点。源点就是自来水厂嘛,一切流的产生地点。汇点就是流的终点,流向的地方。知道了这些之后,我们需要知道网络流中的三大原则。

3.网络流中的三大原则:1.容量原则:网络流中的流量不能大于当前的边能够承载的最大流量,这是很easy的,水管肯定是有粗细的吧,你不能把水管撑爆。2.流量守恒原则:这里是假设流在流动的过程中没有损失,对于任意一个节点,流入的流量和流出的流量数值上是相等的。这个也是很显然的,在每一个转折点,不能有积水,否则越积越多,一会就把这里给撑爆了。3.斜对称性:对于每一条边,从u到v流了x的流量,那么就相当于从v到u留了-x。这也是很显然的,至于怎么定义负的流量,我们后边再说。

4.图的割。这个是图论的基本知识,我就不再详细的说明了,简要的提一下,图的割分为边割集和点割集,我们这里研究的是边割集。什么叫做图的边割集呢?首先是一个集合,这个集合是一些边,这些边满足一个条件。这个条件就是,把这些边从图中删除以后图不再联通了。

5.最小割。这个十分的重要,就是说在图中删除边权最小的边是的图不连通。

6.最大流:这个就是我们要求的东西了,从源点流出流,在满足三个原则的情况下能够在汇点得到的最大的流量是多少?这个是网络流需要研究的问题。

7.求解一个图的最大流有很多算法,我们今天结合HDU上的一个模板题介绍一下EK算法和Dinic算法。

8.EK算法:首先我们需要知道增广路这个概念,EK算法首先找到一条从源点到汇点的流,这条流的流量可能是0,也可能是任意值,但是一定满足上边的三个原则。找到之后,在这条路上找到最小的边容量,把它的流量增加到最大值之后计算流量,这样就会得到一个大的流,这就叫做增广路。我们对每一条路增广,直到图中不存在增广路,这样就求出了最大流(这是一个结论,图中不存在增广路时图的流就是最大流)。至于如何求增广路,我们采用bfs。搜出源点到汇点的所有路径,然后依次增广。但是这里面有很多细节需要处理。比如,我们在增广的时候,修改当前边的流量的时候,我们一定要加上反向流量。一会代码会有体现。

9.Dinic算法。Dinic算法首先会做一遍bfs,但是这个bfs不是搜路径,而是给所有点加上标记,这个标记是记录当前点距离源点的距离,这个距离的定义是从源点到当前点需要经过几条边,我这样说距离是不合适的,应该叫做层次。分层之后,使用dfs找出到源点到汇点的网络流。

下面是HDU1532的两份代码:

//EK算法
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
#include <cmath>
#include <vector>
#include <algorithm>
using namespace std;
#define M 205
#define inf 1e8
int n,m,a,b,c,g[M][M],flow[M],pre[M];
bool vis[M];
int bfs()
{
memset(vis,false,sizeof(vis));
memset(pre,-1,sizeof(pre));
for(int i=1;i<=n;i++)
flow[i]=inf;
vis[1]=true;
queue<int>q;
q.push(1);
while(!q.empty())
{
int p=q.front();
q.pop();
for(int i=1;i<=n;i++)
{
if(!vis[i]&&g[p][i]>0)
{
vis[i]=true;
flow[i]=min(flow[p],g[p][i]);
pre[i]=p;
q.push(i);
}
}
}
if(!vis[n]||n==1) return -1;
return flow[n];
}
int ek()
{
int maxflow=0,d,res,temp;
while((d=bfs())!=-1)
{
maxflow+=d;
temp=n;
while(temp!=1)
{
res=pre[temp];
g[res][temp]-=d;
g[temp][res]+=d;
temp=res;
}
}
return maxflow;
}
int main()
{
//freopen("in.txt","r",stdin);
while(~scanf("%d%d",&m,&n))
{
memset(g,0,sizeof(g));
while(m--)
{
scanf("%d%d%d",&a,&b,&c);
g[a][b]+=c;
}
printf("%d\n",ek());
}
return 0;
}

 

//Dinic算法
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
#include <cmath>
#include <vector>
#include <algorithm>
using namespace std;
#define M 205
#define inf 1e8
int n,m,dis[M],r,cur[M];
struct edge
{
int u,v,cap;
edge(){}
edge(int a,int b,int c){u=a;v=b;cap=c;}
}eg[M*2];
vector<int>g[M];
void addedge(int a,int b,int c)
{
g[a].push_back(r);
eg[r++]=edge(a,b,c);
g[b].push_back(r);
eg[r++]=edge(b,a,0);
}
bool bfs()
{
memset(dis,0,sizeof(dis));
dis[1]=1;
queue<int>q;
q.push(1);
while(!q.empty())
{
int x=q.front();
q.pop();
for(int i=0;i<g[x].size();i++)
{
edge &e=eg[g[x][i]];
if(!dis[e.v]&&e.cap>0)
{
dis[e.v]=dis[x]+1;
q.push(e.v);
}
}
}
return dis[n];
}
int dfs(int x,int low)
{
if(x==n||!low) return low;
int p;
for(int i=cur[x];i<g[x].size();i++)
{
cur[x]=i;
edge &e=eg[g[x][i]];
if(e.cap>0&&dis[e.v]==dis[x]+1&&(p=dfs(e.v,min(low,e.cap))))
{
e.cap-=p;
eg[g[x][i]^1].cap+=p;
return p;
}
}
return 0;
}
void dinic()
{
int ans=0,now;
while(bfs())
{
memset(cur,0,sizeof(cur));
while(now=dfs(1,inf)) ans+=now;
}
printf("%d\n",ans);
}
int main()
{
//freopen("in.txt","r",stdin);
while(~scanf("%d%d",&m,&n))
{
int a,b,c;
r=0;
for(int i=1;i<=n;i++)
g[i].clear();
while(m--)
{
scanf("%d%d%d",&a,&b,&c);
addedge(a,b,c);
}
dinic();
}
return 0;
}