网络流

  

网络流是一种类比水流的解决问题的方法。网络流有三个性质:容量限制、对称性和收支平衡。

一、容量限制

对于图中的每条边(u,v),它都有一个边权c(u,v),代表这条边的最大限流量,而容量限制则是指这条边的实际流量要小于等于最大限流量也就是f(u,v)<=c(u,v).

二、对称性

在网络流的建图过程中,每一条边都需要建一条初始边权值为0的回向边,而当一条边有一定流量时,它的回向边也要改为对应的流量,也就是f(u,v)=-f(v,u)

三、收支平衡

对于既不是源点也不是汇点的任意结点,它的流入流量总和要等于它流出流量的总和。

满足这三个性质,就是一个合法的网络流。

下面就要讲一下网络流的dinic算法:

dinic算法的基本思路就是利用bfs来搜索出从源点到汇点是否有一条未满流的路径,然后在利用dfs来遍历所有未满流路径,重复以上过程直到再没有可以bfs的路径。

而它的时间复杂度是O(n^2*m),实际上,理论数值所对应的数据几乎不会出现,通常并不会像理论数值说的那么慢,只会更快。

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
int next[100001];
int to[100001];
int val[100001];
int head[100001];
int tot=1;
int q[100001];
int s1[100001];
int s2[100001];
int bak[100001];
int n,m;
int S,T;
int x,y;
int ans=0;
int d[100001];
void add(int x,int y,int v)
{
    tot++;
    next[tot]=bak[x];
    bak[x]=tot;
    to[tot]=y;
    val[tot]=v;
    tot++;
    next[tot]=bak[y];
    bak[y]=tot;
    to[tot]=x;
    val[tot]=0;
} 
bool bfs(int S,int T)
{
    int r=0;
    int l=0;
    memset(d,-1,sizeof(d));
    q[r++]=T;
    d[T]=2;
    while(l<r)
    {
        int now=q[l];
        for(int i=bak[now];i;i=next[i])
        {
            if(d[to[i]]==-1&&val[i^1]!=0)
            {
                d[to[i]]=d[now]+1;
                q[r++]=to[i];
            }
        }
        l++;
    }
    if(d[S]==-1)
    {
        return false;
    }
    else
    {
        return true;
    }
}
int dfs(int x,int flow)
{
    if(x==T)
    {
        return flow;
    }
    int now_flow;
    int used=0;
    for(int &i=head[x];i;i=next[i])
    {
        if(d[to[i]]==d[x]-1&&val[i]!=0)
        {
            now_flow=dfs(to[i],min(flow-used,val[i]));
            val[i]-=now_flow;
            val[i^1]+=now_flow;
            used+=now_flow;
            if(now_flow==flow)
            {
                return flow;
            }
        }
    }
    if(used==0)
    {
        d[x]=-1;
    }
    return used;
}
void dinic()
{
    while(bfs(S,T)==true)
    {
    	memcpy(head,bak,sizeof(bak));
        ans+=dfs(S,0x3f3f3f);
    }
}
int main()
{
    scanf("%d%d",&m,&n);
    S=n+1;
    T=n+2;
    while(1)
    {
        scanf("%d%d",&x,&y);
        if(x==-1&&y==-1)
        {
            break;
        }
        add(x,y,0x3f3f3f);
    }
    for(int i=1;i<=n;i++)
    {
        if(i<=m)
        {
            add(S,i,1);
        }
        else
        {
            add(i,T,1);
        }
    }
    dinic();
    if(ans==0)
    {
        printf("No Solution!");
        return 0;
    }
    else
    {
        printf("%d\n",ans);
        for(int i=2;i<=tot;i+=2)
        {
            if(to[i]!=S&&to[i]!=T&&to[i+1]!=S&&to[i+1]!=T)
            {
                if(val[i+1]!=0)
                {
                    printf("%d %d\n",to[i+1],to[i]);
                }
            }
        }
    }
    return 0;
}

这里有几个优化;

1、从汇点倒着bfs,这样能更快查找到有没有连接汇点且没满流的边。

2、当dfs过程中,如果一个点不能再往下走,那么将这个点深度变为-1,下次dfs就不会再从这个点往下搜。

3、当当前流量等于最大流量时,就不用再往下dfs了,可以直接回溯。

有的题目中,图中的点也有流量限制,那么就把这个点拆开成一个入点和一个出点,所有入边指向入点,所有出边从出点出,在两点之间连一条限流的边。

这里要注意:链式前向星建边时,边的编号初始值为1,建边数组要开双倍!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值