memset的一个用法:
memset(0x3f)也可以近似的填充出一个INF,是1e9的数量级(1061109567),而且可以满足加上一个数值(1e9)后仍然是int。具体参见博客 memset函数详解
网络流
最大流的三个算法:朴素FF算法、EK算法、dinic算法
参见博客 网络流基础
dinic算法可以说是朴素FF和Ek算法的优化产品,在一次bfs下进行多次dfs以降低时间复杂度。据此,我们优先使用dinic算法。
核心代码部分:
bool bfs(int s,int t)//bfs功能和EK算法的相似,不同的是Dinic中的bfs要求出所有点到源点s的最短路dis[i]
{
q=queue<int>();//清空队列
memset(dis,-1,sizeof(dis));
dis[s]=0;
q.push(s);
while(!q.empty())
{
int x=q.front();
q.pop();
for(int i=head[x];i>-1;i=e[i].next)
{
int now=e[i].to;
if(dis[now]==-1&&e[i].value!=0)
{
dis[now]=dis[x]+1;
q.push(now);
}
}
}
return dis[t]!=-1;
}
int dfs(int x,int t,int maxflow)//表示从x出发寻找到汇点T的增广路,寻找到maxflow流量为止,并相应的增广。返回值为实际增广了多少(因为有可能找不到maxflow流量的增广路)
{
if(x==t)return maxflow;
int ans=0;
for(int i=head[x];i>-1;i=e[i].next)
{
int now=e[i].to;
if(dis[now]!=dis[x]+1||e[i].value==0||ans>=maxflow)continue;
int f=dfs(now,t,min(e[i].value,maxflow-ans));
e[i].value-=f;
e[i^1].value+=f;
ans+=f;
}
return ans;
}
int Dinic(int s,int t)
{
int ans=0;
while(bfs(s,t))
ans+=dfs(s,t,INF);
return ans;
}
有一些值得注意的地方:
- 网络流问题中的反向弧为dfs提供了反悔的机会,类似于矢量的抵消,让流量可以从上一个结点重新流动。
- bfs中清空队列的手法值得一学。
- 在dfs的参数中,maxflow表示的是当前结点x最多可以获得的流量,而在函数体中的f用来存储每一个邻点需求的流量,ans表示在maxflow约束下可以增广的流量总和。可以肯定的是,ans >= maxflow的时候,再往后的讨论已经没有意义了,从而continue即可。自然地,dfs开始时maxflow应该是INF。
在洛谷上做到了一个二分匹配的题目 飞行员匹配问题
由于反向弧具有 抵消 之前流的功用,我们最后只需要遍历所有的反向边,检查它的权值是不是1即可。
更一般地说,具有正权值的反向弧实则就是记录了最大流的路径问题。
费用流
推荐两篇博客:
https://www.cnblogs.com/widerg/p/9394929.html
https://www.cnblogs.com/fzl194/p/8859308.html (该系列还包含了有上下限的最大流)
核心代码:
struct Pre{int id;int node;}pre[M<<1];
bool spfa(int s,int t)
{
q=queue<int>();
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));
memset(pre,-1,sizeof(pre));
memset(minv,0x3f,sizeof(minv));
dis[s]=0;
vis[s]=1;
q.push(s);
while(!q.empty())
{
int x=q.front();
q.pop();
vis[x]=0;
for(int i=head[x];i>-1;i=e[i].next)
{
int now=e[i].to;
if(dis[now]>dis[x]+e[i].cost&&e[i].value)
{
dis[now]=dis[x]+e[i].cost;
minv[now]=min(minv[x],e[i].value);
pre[now].id=i;
pre[now].node=x;
if(!vis[now])
{
vis[now]=1;
q.push(now);
}
}
}
}
return dis[t]!=INF;
}
void MCMF(int s,int t,int &maxflow,int &mincost)
{
while(spfa(s,t))
{
for(int i=t;i!=s;i=pre[i].node)
{
e[pre[i].id].value-=minv[t];
e[pre[i].id^1].value+=minv[t];
}
maxflow+=minv[t];
mincost+=minv[t]*dis[t];
}
}
VJ
kuangbin网络流
A
第一个没有参考别的代码自己写出来的dinic算法,同时也实践了一下反向弧求路径。但是非常不幸的是,这道题不能直接用最大流做,需要用到一个叫做 拆点 的方法。我们注意到,一般的最大流问题是在边上对流量有限制,流经某条边的流量不能超过其容积;但是这道题不是对边上的限制,而是对结点本身的限制(某个机器生产的零件总值是一个给定值)。开始重新dfs学习新知识点
参见kuangbin本人博客 POJ 3436 ACM Computer Factory(最大流)