网络流(一)

网络流(一)介绍

参考:https://www.cnblogs.com/ZJUT-jiangnan/p/3632525.html

定义:

    源点:提供初始流量的点,只出不进。
    汇点:所有可用流最后的聚集点,只进不出。
    容量:点与点之间可以流的最大值(理论值)。
    流量:点与点之间的实际流量,当该边的流量等于容量,说明满流。
    最大流:从源点出发,汇点可以接受到的最大流量。
    可行流:一组边,其流量都没有超过最大流就说明是可行流。

最大流的求解思路

     一:首先最基础的就是找到一条可行流,我们可以初始流量为1或者0,这个用一个bfs就可以完成,从源点出发,
     两点有连边就继续搜,直到搜到汇点,这样bfs出来的结果就是一条可行流。
     二:既然我们要找的是最大流,我们就要尽可能得增加这条可行流的流量,我们先对每条边求一个值P(可能增加
     的流量),既容量-当前的流量,然后我们可以知道,对于一条可行流,我们对每条边增加所有边的P中最小的那个,
     那么这个流依旧是一条可行流。
     三:这样的话我们就可以对这条可行流加上一个最小的P,那么他的流量就是他原来的流量+P,可以进行这种操作
     的路我们称其为增广路。那我们求解最大流,就是不断地从源点到汇点找这样的增广路。
     四:直到找不到增广路,既不再存在可能增加流量的一条从源点到汇点的可行流存在,那我们这个求最大流的过程
     就完成了。

相关代码:

bfs找增广路:

int bfs()
{
    queue <int> q;
    memset(dis,-1,sizeof(dis));
    dis[sp]=0;
    q.push(sp);
    while(!q.empty())
    {
        int cur=q.front();
        q.pop();
        for(int i=head[cur];i!=-1;i=edge[i].next)
        {
            int u=edge[i].v;
            if(dis[u]==-1 && edge[i].c>0)
            {
                dis[u]=dis[cur]+1;
                q.push(u);
            }
        }
    }
    return dis[tp] != -1;
}

dfs尝试增加增广路的流量,填充的流量在反向边中加上:

int dfs(int a,int b)
{
    int r=0;
    if(a==tp)return b;
    for(int i=head[a];i!=-1 && r<b;i=edge[i].next)
    {
        int u=edge[i].v;
        if(edge[i].c>0 && dis[u]==dis[a]+1)
        {
            int x=min(edge[i].c,b-r);
            x=dfs(u,x);
            r+=x;
            edge[i].c-=x;
            edge[i^1].c+=x;
        }
    }
    if(!r)dis[a]=-2;
    return r;
}

建边

struct Edge{
    int u,v,c;
    int next;
}edge[maxn*maxn];
int n,m;
int edn;//边数
int head[maxn*maxn],dis[maxn*maxn];
int sp,tp;//原点,汇点

void addedge(int u,int v,int c)
{
    edge[edn].u=u; edge[edn].v=v; edge[edn].c=c;
    edge[edn].next=head[u]; head[u]=edn++;

    edge[edn].u=v; edge[edn].v=u; edge[edn].c=0;
    edge[edn].next=head[v]; head[v]=edn++;
}
为什么要建反向边?

为了修正当前的增加的流量,因为我们寻找增广路是任意的,而实际想要达到最大流增广路的寻找是有顺序的,
所以我们增加反向边,可以让后面的流进行调整,这样回流后的结果和实际正确顺序模拟出来的结果是一致的。
相当于你本来实际需要流的值,别人帮你流了,那你需要找出那条别人本应该流的路径,做一个互补。具体的
内容可以看开头的博客,有图片解析。

完整代码:
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn=505;
const int inf=0x3f3f3f3f;
struct Edge{
    int u,v,c;
    int next;
}edge[maxn*maxn];
int n,m;
int edn;//边数
int head[maxn*maxn],dis[maxn*maxn];
int sp,tp;//原点,汇点

void addedge(int u,int v,int c)
{
    edge[edn].u=u; edge[edn].v=v; edge[edn].c=c;
    edge[edn].next=head[u]; head[u]=edn++;

    edge[edn].u=v; edge[edn].v=u; edge[edn].c=0;
    edge[edn].next=head[v]; head[v]=edn++;
}
int bfs()
{
    queue <int> q;
    memset(dis,-1,sizeof(dis));
    dis[sp]=0;
    q.push(sp);
    while(!q.empty())
    {
        int cur=q.front();
        q.pop();
        for(int i=head[cur];i!=-1;i=edge[i].next)
        {
            int u=edge[i].v;
            if(dis[u]==-1 && edge[i].c>0)
            {
                dis[u]=dis[cur]+1;
                q.push(u);
            }
        }
    }
    return dis[tp] != -1;
}
int dfs(int a,int b)
{
    int r=0;
    if(a==tp)return b;
    for(int i=head[a];i!=-1 && r<b;i=edge[i].next)
    {
        int u=edge[i].v;
        if(edge[i].c>0 && dis[u]==dis[a]+1)
        {
            int x=min(edge[i].c,b-r);
            x=dfs(u,x);
            r+=x;
            edge[i].c-=x;
            edge[i^1].c+=x;
        }
    }
    if(!r)dis[a]=-2;
    return r;
}

int dinic(int sp,int tp)
{
    int total=0,t;
    while(bfs())
    {
        while(t=dfs(sp,inf))
        total+=t;
    }
    return total;
}
int main()
{
    int i,u,v,c;
    scanf("%d%d",&n,&m);
    edn=0;//初始化
    memset(head,-1,sizeof(head));
    // sp=1;tp=n;建边
    	....
    	....
    	....
    //输出匹配结果
    int ans=dinic(sp,tp);
    printf("%d\n",ans);
    if(ans==0)
    {
        printf("No Solution\n");
        return 0;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值