2019暑训8月5号 网络流

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;
}

有一些值得注意的地方:

  1. 网络流问题中的反向弧为dfs提供了反悔的机会,类似于矢量的抵消,让流量可以从上一个结点重新流动。
  2. bfs中清空队列的手法值得一学。
  3. 在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(最大流)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值