学习笔记——网络流

本文仅仅记录做法或代码,至于标准证明等并不会出现(反正也是给自己看的qwq)

网络最大流

bool bfs(int s,int t)
{
    for(int i=1;i<=n;++i) cur[i]=head[i];
    memset(dep,0,sizeof(dep));
    queue<int> q;
    q.push(s); dep[s]=1;
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int i=head[u];i;i=edge[i].next)
        {
            int v=edge[i].to;
            if(!dep[v]&&edge[i].flow)
            {
                dep[v]=dep[u]+1;
                q.push(v);
                if(v==t) return true;
            }
        }
    }
    return false;
}
int dfs(int s,int t,int u,int rest)
{
    if(u==t) return rest;
    int used=0;
    for(int i=cur[u];i;i=edge[i].next)
    {
        cur[u]=i;
        int v=edge[i].to;
        if(dep[v]==dep[u]+1&&edge[i].flow)
        {
            int k=dfs(s,t,v,min(edge[i].flow,rest-used));
            if(!k) dep[v]=0;
            used+=k;
            edge[i].flow-=k;
            edge[i^1].flow+=k;
        }
    }
    return used;
}
int dinic(int s,int t)
{
    int maxflow=0,flow;
    while(bfs(s,t)) while(flow=dfs(s,t,s,INF)) maxflow+=flow;
    return maxflow; 
}

最小费用最大流

仅仅只是将bfs变为spfa,但是将原来的多路增广又打回单路增广

bool Spfa(int s,int t)
{
    memset(dis,100,sizeof(dis));
    memset(flow,100,sizeof(flow));
    memset(exist,0,sizeof(exist));
    queue<int> q;
    pre[t]=-1; exist[s]=1; dis[s]=0; q.push(s);
    while(!q.empty())
    {
        int u=q.front();q.pop();exist[u]=0;
        for(int i=head[u];i;i=edge[i].next)
        {
            int v=edge[i].to;
            if(edge[i].flow>0&&dis[v]>dis[u]+edge[i].dis)
            {
                dis[v]=dis[u]+edge[i].dis;
                flow[v]=min(edge[i].flow,flow[u]);
                pre[v]=u;
                preedge[v]=i;
                if(!exist[v])
                {
                    exist[v]=1;
                    q.push(v);
                }
            }
        }
    }
    return pre[t]!=-1;
}
void MCMF()
{
    while(Spfa(s,t))
    {
        maxflow+=flow[t];
        mincost+=flow[t]*dis[t];
        int now=t;
        while(now!=s)
        {
            edge[preedge[now]].flow-=flow[t];
            edge[preedge[now]^1].flow+=flow[t];
            now=pre[now];
        }
    }
}

无源汇上下界可行流

需要保证每个点都有入度和出度才可以回环往复保证流量守恒

先将每条边流量初始赋值为下界,然后问题就是有些点不遵从流量守恒

解决方案:

建立超级源汇点S,T,对于入流量大于出流量的点i,为了使入流量=出流量,我们将S给i连一条容量为(入-出)的边,从而在不改变原图流入量的同时通过引入外资增加流出量;对于入流量小于出流量的点i,同理用i连T一条容量为(出-入)的边(可以理解为通过这条边引导入流量增加,然后删掉这个不存在的边原图就守恒了)

连边(d=入-出)

for(int i=1;i<=n;++i)
    {
        if(d[i]>0) ad(S,i,d[i]);
        if(d[i]<0) ad(i,T,-d[i]);
    }

连完边后跑最大流即可,如果从S出发的边全部满流则为有可行流,原图中每条边的反边的剩余流量即为可行流


有源汇上下界可行流

除了源点和汇点流量不守恒外其余点依旧守恒,然而源点出流和汇点入流相等,所以将t和s用容量INF的边连起来之后就成无源汇上下界可行流


上下界最大流

用无源汇上下界可行流的方法连边后从S跑最大流,得到flow1(可行流),删去S,T和其所连边,从s再跑一次最大流得到flow2(剩余流量),答案为flow1+flow2


上下界最小流

用上面方法得到可行流flow1之后(就可以不要了),用t向s连一条INF的边(注意是小写),然后再跑一次最大流,之前连的边的反边即为答案

    dinic(S,T);
    ad(t,s,INF);
    dinic(S,T);
    cout<<edge[cnt].flow<<endl;

上下界最小费用流

连边方法同上下界最小流,将求最大流改为求最小费用最大流

答案为每条边的下界*单价+最小费用流

AHOI2014/JSOI2014支线剧情

求从1出发的多条路径覆盖所有边至少一次的最小代价

    read(n);
    s=1;t=n+1;S=n+2;T=n+3;
    for(int i=1;i<=n;++i)
    {
        read(m);
        while(m--)
        {
            int x,t; read(x);read(t);
            //单价为t
            ins(i,x,1,INF,t);
            ans+=t;//下界为1,所以只加t*1
        }
    }
    for(int i=1;i<=n+1;++i)
    {
        if(d[i]>0) ad(S,i,d[i],0);
        if(d[i]<0) ad(i,T,-d[i],0);
    }
    ad(t,s,INF,0);//连边跑费用流
    ans+=MCMF(S,T);
    cout<<ans<<endl;
posted @ 2019-06-12 10:53  擅长平地摔的艾拉酱  阅读(228)  评论(1编辑  收藏  举报
/*取消选中*/