不得不复习一下网络流了,先复习最大流模板吧
最主要的就是dfs和bfs过程吧
bfs是为了分层和判断是否需要继续
显然对于一个图,分层之后,对于任一一边,起点要么加一等于终点,要么终点小于起点
终点小于起点的显然已经计算过,不需要松弛()
判断是否需要继续增广实际上是看终点是否有depth就可以解决的
再说dfs的过程
是通过当前的可以下放的流量dfs下去求得可以达到的流量,回溯上来统计sum,使得始终不超过限制
详细见代码吧,其实复习了一下感觉挺简单的
#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
struct Edge
{
int to,val,next;
}e[200010];
int n,m,s,t,num;
int cur[100010],head[100010],depth[100010];
void addedge(int a,int b,int v)
{
e[num].to=b;
e[num].next=head[a];
e[num].val=v;
head[a]=num++;
}
int dfs(int u,int val)//val是当前可以向前流的流量,返回的是实际流的流量
{
if(u==t||val==0)//如果到了终点或者没有可以继续的流量了,就return
{
return val;
}
int sum=0;//分配出去的流量
for(int &i=cur[u];i!=-1;i=e[i].next)
{
if(depth[e[i].to]==depth[u]+1&&e[i].val>0)//分层图下必须要depth向下才进行,不然无意义
{
int f=dfs(e[i].to,min(val-sum,e[i].val));//贪心的向下一个点传输可以传输的最大的流量,返回来到底可以传多少就是下一层的事情了
if(f>0)//如果真的传输过去了,更改当前边和反边
{
e[i].val-=f;
e[i^1].val+=f;
sum+=f;//记录分配除去的流量
if(val==sum) return sum;//分配完了就返回
}
}
}
return sum;//返回实际分配除去的流量
}
bool bfs()//这个函数的意义在于用分层图优化时间,并且预先判断还有没有余留
{
memset(depth,0,sizeof(depth));//这里一定要memset
queue <int>q;
depth[s]=1;
q.push(s);//塞入起点
while(!q.empty())
{
int now=q.front();
q.pop();
for(int i=head[now];i!=-1;i=e[i].next)
{
if(depth[e[i].to]==0&&e[i].val>0)//如果没来到过,并且可以流过这个边
{
depth[e[i].to]=depth[now]+1;
q.push(e[i].to);//继续下一层
}
}
}
return depth[t];//返回是否可以到达t
}
int main()
{
scanf("%d%d%d%d",&n,&m,&s,&t);
memset(head,-1,sizeof(head));//毒瘤的反悔边让我改变了码风
for(int i=1;i<=m;i++)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
addedge(a,b,c);
addedge(b,a,0);
}
int ans=0;
while(bfs())
{
for(int i=1;i<=n;i++)
{
cur[i]=head[i];//清空当前弧,如果是不能跑的边,边权已经是0了,这样反而会漏掉
}
ans+=dfs(s,0x3f3f3f3f);
}
printf("%d
",ans);
}
这里总结一下网络最大流的建模思想
直接考察最大流
这种一般考察很少
最大流最小割定理
个人认为比较重要,典型的就有三种
- 小行星
- 集合与单个的贡献矛盾
- 二部图的最小权点覆盖问题
其他都差不多了