网络流0.2

网络流

0.2


Table of Contents

网络流

基本介绍

代码实现(p3376)

DINIC

EK

 


基本介绍

在一定容量在众多路径中找最大流量,思路:增广(bfs)+反向弧,代码+注解就能懂的基本操作.

简单解释反向弧,就是在发现一步操作不最优时,不撤回而是往回走一步抵消其影响.

不懂?贴两个温馨提示:

代码+注解

原理解释


代码实现(p3376)

 


DINIC

最常见的算法,利用分层图思想.下面函数dinic()内嵌了一个dfs,因为dfs是dinic区别ek的点.

在普通情况下, DINIC算法时间复杂度为O(V2E)
在二分图中, DINIC算法时间复杂度为O(√VE)

//dinic
//经测试手写队列较省时空
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
int n,m,s,t,ro=-1;
int first[200005],d[10005];
struct $
{
    int t,c,f,ne;
}a[200005];
int q[10005],head,tail;
void road(int x,int y,int c)
{
    a[++ro].t=y;
    a[ro].c=c;
    a[ro].ne=first[x];
    first[x]=ro;
}
bool bfs()
{
    memset(d,0,sizeof(d));
    head=0;tail=1;
    q[1]=s;
    d[s]=1;
    while(head<tail)
    {
        int x=q[++head];
        for(int i=first[x];i!=-1;i=a[i].ne)
        {
            int t1=a[i].t;
            if(a[i].c>a[i].f&&!d[t1])
            {
                d[t1]=d[x]+1;
                q[++tail]=t1;
            }
        }
    }
    return d[t];
}
int dinic(int x,int flow)
{
    if(x==t) return flow;
    int ans=0;
    for(int i=first[x];i!=-1&&ans<flow;i=a[i].ne)
    {
        int t1=a[i].t;
        if(a[i].c>a[i].f&&d[t1]==d[x]+1)
        {
            int sum=dinic(t1,min(flow-ans,a[i].c-a[i].f));
            ans+=sum;
            a[i].f+=sum;
            a[i^1].f-=sum;//反向弧
        }
    }
    return ans;
}
int main()
{
    scanf("%d %d %d %d\n",&n,&m,&s,&t);
    memset(first,-1,sizeof(first));
    for(int i=1;i<=m;i++)
    {
        int x,y,c;
        scanf("%d %d %d\n",&x,&y,&c);
        road(x,y,c);road(y,x,0);
    }
    int ans=0;
    while(bfs())
        ans+=dinic(s,99999999);
    printf("%d\n",ans);
    return 0;
}


EK

EK简单地说,就是通过bfs每次找一遍增广路径,算法的复杂度上限为ve^2.由于每次都要从头开始,可以处理加边的情况.


应用

二分图匹配(P3386)

将左半部分和右半部分根据题目所边连接,直接暴力跑dicinic.注意汇点t=n+m+2,特别注意反向弧编号.

#include <cstdio>
#include <iostream>
#include <cmath>
#include <cstring>
#include <queue>
using namespace std;
int fi[1000003],n,ro;
struct $
{
    int t,ne,c,re;
}a[1000003];
void road(int x,int y,int c,int re)
{
    a[++ro].ne=fi[x];
    a[ro].t=y;
    a[ro].c=c;
    a[ro].re=re;
    fi[x]=ro;
}
int d[1000003];
int q[1000003];
bool bfs()
{
    memset(d,0,sizeof(d));
    int he=0,ta=1; d[1] = 1;
    q[he]=1;
    while(he<ta)
    {
        int x = q[he++];
        for(int i=fi[x]; i; i=a[i].ne)
        {
            if(a[i].c>0 && !d[a[i].t])
            {
                d[a[i].t] = d[x]+1;
                q[ta++]=a[i].t;
            }
        }
    }
    return d[n];
}
int dicnic(int x,int flow)
{
    if(x==n) return flow;
    int ans = flow;
    for(int i=fi[x]; i; i=a[i].ne)
    {
        if(d[a[i].t]==d[x]+1 && a[i].c>0 && ans)
        {
            int sum = dicnic(a[i].t,min(a[i].c,ans));
            if(sum>0)
            {
                a[i].c -= sum;
                ans -= sum;
                a[a[i].re].c += sum;
            }
        }
    }
    return flow-ans;
}
int main()
{
    int n1,m,e;
    scanf("%d%d%d",&n1,&m,&e);
    n = n1+m+2;
    for(int i=1; i<=e; i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        if(x<=n1 && y<=m)
        {
            road(x+1,y+n1+1,1,ro+2);
            road(y+n1+1,x+1,0,ro);
        }
    }
    for(int i=1; i<=n1; i++)
    {
        road(1,i+1,1,ro+2);
        road(i+1,1,0,ro);
    }
    for(int i=1; i<=m; i++)
    {
        road(i+n1+1,n,1,ro+2);
        road(n,i+n1+1,0,ro);
    }
    int ans=0;
    while(bfs())
        ans += dicnic(1,0x7fffffff);
    printf("%d\n",ans);
    return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值