如果边集合E含有一条边(u,v);必然不存在反方向的边(v,u);我们称这样的有向带权图为网络
网络是一个有向带权图,包含一个源点和一个汇点,没有反向平行边。
网络流即网络上的流,是定义在网络边集的一个非负函数;
可行流:满足容量约束(实际流量<=该管道最大容量)和流量守恒(除源点和汇点外,所有内部结点流入量=流出量)
网络最大流:在满足容量约束和流量约束的前提下,在流网络中找到一个净输出最大的网络流。
在残余网络中,与网络边对应的同向边是可增量(即还可以增加多少流量),反向边是实际流量。
可增广路:是残余网络中一条能从源点S到汇点T的简单路径。
引入反向边:
为什么要引入反向边呢?如图:
我们第一次找到了1-2-3-4这条增广路,这条路上的delta值显然是1。于是我们修改后得到了下面这个流。(图中的数字是容量)
这时候(1,2)和(3,4)边上的流量都等于容量了,我们再也找不到其他的增广路了,当前的流量是1。
但这个答案明显不是最大流,因为我们可以同时走1-2-4和1-3-4,这样可以得到流量为2的流。
应该有一个不走(2-3-4)而改走(2-4)的机制。那么如何解决这个问题呢?
利用了一个叫做反向边的概念来解决这个问题。即每条边(I,j)都有一条反向边(j,i),反向边也同样有它的容量。
这时再找增广路的时候,就会再找到1-3-2-4这条可增广量,即delta值为1的可增广路。将这条路增广之后,得到了最大流2。
(利用反向边,使程序有了一个后悔和改正的机会.)
最大流--EK:
每次bfs判断存不存在增广路(可以源点s到达汇点t的路径),记录找到路径中的最小flow,
每次ans+=flow,直到再也找不到可增广路。
int g[maxn][maxn];//记录边容量
int vis[maxn];//标记
int pre[maxn];//pre[i]表示i的前缀
int bfs()
{
memset(vis,0,sizeof(vis));
memset(pre,-1,sizeof(pre));
queue<int >q;
while(!q.empty()){q.pop();}
q.push(st);
vis[st]=1;
while(!q.empty())
{
int now=q.front();
q.pop();
for(int i=st;i<=ed;i++)
{
if(!vis[i]&&g[now][i])
{
vis[i]=1;
pre[i]=now;
if(i==ed)//如果此次bfs能从s到t,返回1
return 1;
q.push(i);
}
}
}
return 0;
}
int EK()
{
int v,d,maxflow=0;
while(bfs())
{
v=ed;
d=inf;
while(v!=st)
{
d=min(d,g[pre[v]][v]);
v=pre[v];
}
maxflow+=d;
v=ed;
while(v!=st)
{
g[pre[v]][v]-=d;//每次找到一条路,这条路每条正向边容量减少d
g[v][pre[v]]+=d;//反向边容量减少d
v=pre[v];
}
}
return maxflow;
}
dinic:
- 用BFS建立分层图 注意:分层图是以当前图为基础建立的,所以要重复建立分层图,(分层图:从源点到当前点的最近距离,可以存储到一个数组中,距离源点相等的点dis[]相等)
- 用DFS的方法寻找一条由源点到汇点的路径,获得这条路径的流量I 根据这条路径修改整个图,将所经之处正向边流量减少I,反向边流量增加I,注意I是非负数 DFS的下一个节点的Dis(距源点的距离)要比自己的Dis大1
重复步骤2,直到DFS找不到新的路径时,重复步骤1
而层次图作用:
1、在DFS增广时,当且仅当下一个点的层次是当前点的下一层才进入下一个点;
2、是用来判断源点到汇点是否还有流量可以流。如果汇点已经不再层次图上了,说明没有多余的流量可以从源点流到汇点了, 这个时候就可以结束搜索而输出答案了。
int g[maxn][maxn];
int dis[maxn];
int n,m,k,mm;//mm是汇点
int bfs()
{
memset(dis,0,sizeof(dis));
queue<int >q;
while(!q.empty()){q.pop();}
q.push(1);
dis[1]=1;
while(!q.empty())
{
int now=q.front();
q.pop();
for(int i=1;i<=mm;i++)
{
if(!dis[i]&&g[now][i]>0)
{
dis[i]=dis[now]+1;
q.push(i);
}
}
}
if(dis[mm]) return 1;
return 0;
}
int dfs(int s,int u)
{
if(s==mm) return u;
int tmp=u;
for(int i=1;i<=mm&&tmp;i++)
{
if(dis[i]==dis[s]+1&&g[s][i]>0)
{
int t=dfs(i,min(g[s][i],tmp));
g[s][i]-=t;
g[i][s]+=t;
tmp-=t;;//从源点到s的瓶颈也会更小(减少t )
}
}
return u-tmp;
}
int dinic()
{
int t=0,ans=0;
while(bfs())
{
while(t=dfs(1,inf))
ans+=t;
}
return ans;
}
dinic邻接表做法:
///每组数据 first初始化为-1;cnt起始值设为1;
int dis[maxn];
char s[maxn];
int n,f,c,ed;
struct node
{
int v;
int cap;
int next;
};
int first[maxn];
node g[M];
int cnt;
void addedge(int u,int v,int cap)
{
g[cnt].v=v;
g[cnt].cap=cap;
g[cnt].next=first[u];
first[u]=cnt++;
g[cnt].v=u;
g[cnt].cap=0;
g[cnt].next=first[v];
first[v]=cnt++;
}
int bfs()
{
memset(dis,0,sizeof(dis));
queue<int >q;
while(!q.empty()){q.pop();}
q.push(0);
dis[0]=1;
while(!q.empty())
{
int u=q.front();
q.pop();
for(int e=first[u];~e;e=g[e].next)
{
int v=g[e].v;
int cap=g[e].cap;
if(!dis[v]&&cap>0)
{
dis[v]=dis[u]+1;
q.push(v);
}
}
}
if(dis[ed]) return 1;
return 0;
}
int dfs(int u,int flow)
{
if(u==ed) return flow;
int t;
//int tmp=u;
for(int e=first[u];~e;e=g[e].next)
{
int v=g[e].v;
int cap=g[e].cap;
if(dis[v]==dis[u]+1&&cap>0&&(t=dfs(v,min(cap,flow))))
{
g[e].cap-=t;
g[e^1].cap+=t;
return t;
//tmp-=t;
}
}
dis[u]=-1;
return 0;
}
int Ans()
{
int ans=0,t=0;
while(bfs())
{
while(t=dfs(0,inf))
ans+=t;
}
return ans;
}