网络流(一)介绍
参考:https://www.cnblogs.com/ZJUT-jiangnan/p/3632525.html
定义:
源点:提供初始流量的点,只出不进。
汇点:所有可用流最后的聚集点,只进不出。
容量:点与点之间可以流的最大值(理论值)。
流量:点与点之间的实际流量,当该边的流量等于容量,说明满流。
最大流:从源点出发,汇点可以接受到的最大流量。
可行流:一组边,其流量都没有超过最大流就说明是可行流。
最大流的求解思路
一:首先最基础的就是找到一条可行流,我们可以初始流量为1或者0,这个用一个bfs就可以完成,从源点出发,
两点有连边就继续搜,直到搜到汇点,这样bfs出来的结果就是一条可行流。
二:既然我们要找的是最大流,我们就要尽可能得增加这条可行流的流量,我们先对每条边求一个值P(可能增加
的流量),既容量-当前的流量,然后我们可以知道,对于一条可行流,我们对每条边增加所有边的P中最小的那个,
那么这个流依旧是一条可行流。
三:这样的话我们就可以对这条可行流加上一个最小的P,那么他的流量就是他原来的流量+P,可以进行这种操作
的路我们称其为增广路。那我们求解最大流,就是不断地从源点到汇点找这样的增广路。
四:直到找不到增广路,既不再存在可能增加流量的一条从源点到汇点的可行流存在,那我们这个求最大流的过程
就完成了。
相关代码:
bfs找增广路:
int bfs()
{
queue <int> q;
memset(dis,-1,sizeof(dis));
dis[sp]=0;
q.push(sp);
while(!q.empty())
{
int cur=q.front();
q.pop();
for(int i=head[cur];i!=-1;i=edge[i].next)
{
int u=edge[i].v;
if(dis[u]==-1 && edge[i].c>0)
{
dis[u]=dis[cur]+1;
q.push(u);
}
}
}
return dis[tp] != -1;
}
dfs尝试增加增广路的流量,填充的流量在反向边中加上:
int dfs(int a,int b)
{
int r=0;
if(a==tp)return b;
for(int i=head[a];i!=-1 && r<b;i=edge[i].next)
{
int u=edge[i].v;
if(edge[i].c>0 && dis[u]==dis[a]+1)
{
int x=min(edge[i].c,b-r);
x=dfs(u,x);
r+=x;
edge[i].c-=x;
edge[i^1].c+=x;
}
}
if(!r)dis[a]=-2;
return r;
}
建边
struct Edge{
int u,v,c;
int next;
}edge[maxn*maxn];
int n,m;
int edn;//边数
int head[maxn*maxn],dis[maxn*maxn];
int sp,tp;//原点,汇点
void addedge(int u,int v,int c)
{
edge[edn].u=u; edge[edn].v=v; edge[edn].c=c;
edge[edn].next=head[u]; head[u]=edn++;
edge[edn].u=v; edge[edn].v=u; edge[edn].c=0;
edge[edn].next=head[v]; head[v]=edn++;
}
为什么要建反向边?
为了修正当前的增加的流量,因为我们寻找增广路是任意的,而实际想要达到最大流增广路的寻找是有顺序的,
所以我们增加反向边,可以让后面的流进行调整,这样回流后的结果和实际正确顺序模拟出来的结果是一致的。
相当于你本来实际需要流的值,别人帮你流了,那你需要找出那条别人本应该流的路径,做一个互补。具体的
内容可以看开头的博客,有图片解析。
完整代码:
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn=505;
const int inf=0x3f3f3f3f;
struct Edge{
int u,v,c;
int next;
}edge[maxn*maxn];
int n,m;
int edn;//边数
int head[maxn*maxn],dis[maxn*maxn];
int sp,tp;//原点,汇点
void addedge(int u,int v,int c)
{
edge[edn].u=u; edge[edn].v=v; edge[edn].c=c;
edge[edn].next=head[u]; head[u]=edn++;
edge[edn].u=v; edge[edn].v=u; edge[edn].c=0;
edge[edn].next=head[v]; head[v]=edn++;
}
int bfs()
{
queue <int> q;
memset(dis,-1,sizeof(dis));
dis[sp]=0;
q.push(sp);
while(!q.empty())
{
int cur=q.front();
q.pop();
for(int i=head[cur];i!=-1;i=edge[i].next)
{
int u=edge[i].v;
if(dis[u]==-1 && edge[i].c>0)
{
dis[u]=dis[cur]+1;
q.push(u);
}
}
}
return dis[tp] != -1;
}
int dfs(int a,int b)
{
int r=0;
if(a==tp)return b;
for(int i=head[a];i!=-1 && r<b;i=edge[i].next)
{
int u=edge[i].v;
if(edge[i].c>0 && dis[u]==dis[a]+1)
{
int x=min(edge[i].c,b-r);
x=dfs(u,x);
r+=x;
edge[i].c-=x;
edge[i^1].c+=x;
}
}
if(!r)dis[a]=-2;
return r;
}
int dinic(int sp,int tp)
{
int total=0,t;
while(bfs())
{
while(t=dfs(sp,inf))
total+=t;
}
return total;
}
int main()
{
int i,u,v,c;
scanf("%d%d",&n,&m);
edn=0;//初始化
memset(head,-1,sizeof(head));
// sp=1;tp=n;建边
....
....
....
//输出匹配结果
int ans=dinic(sp,tp);
printf("%d\n",ans);
if(ans==0)
{
printf("No Solution\n");
return 0;
}
return 0;
}