网络流--最大流

如果边集合E含有一条边(u,v);必然不存在反方向的边(v,u);我们称这样的有向带权图为网络

网络是一个有向带权图,包含一个源点和一个汇点,没有反向平行边。

网络流即网络上的流,是定义在网络边集的一个非负函数;

可行流:满足容量约束(实际流量<=该管道最大容量)和流量守恒(除源点和汇点外,所有内部结点流入量=流出量)

网络最大流:在满足容量约束和流量约束的前提下,在流网络中找到一个净输出最大的网络流。

在残余网络中,与网络边对应的同向边是可增量(即还可以增加多少流量),反向边是实际流量。

可增广路:是残余网络中一条能从源点S到汇点T的简单路径。

引入反向边:

为什么要引入反向边呢?如图

我们第一次找到了1-2-3-4这条增广路,这条路上的delta值显然是1。于是我们修改后得到了下面这个流。(图中的数字是容量)

这时候(1,2)和(3,4)边上的流量都等于容量了,我们再也找不到其他的增广路了,当前的流量是1。

但这个答案明显不是最大流,因为我们可以同时走1-2-4和1-3-4,这样可以得到流量为2的流。

应该有一个不走(2-3-4)而改走(2-4)的机制。那么如何解决这个问题呢?

利用了一个叫做反向边的概念来解决这个问题。即每条边(I,j)都有一条反向边(j,i),反向边也同样有它的容量。

这时再找增广路的时候,就会找到1-3-2-4这条可增广量,即delta值为1的可增广路。将这条路增广之后,得到了最大流2。

(利用反向边,使程序有了一个后悔和改正的机会.)

最大流--EK:

每次bfs判断存不存在增广路(可以源点s到达汇点t的路径),记录找到路径中的最小flow,

每次ans+=flow,直到再也找不到可增广路。

 

int g[maxn][maxn];//记录边容量
int vis[maxn];//标记
int pre[maxn];//pre[i]表示i的前缀
int bfs()
{
    memset(vis,0,sizeof(vis));
    memset(pre,-1,sizeof(pre));
    queue<int >q;
    while(!q.empty()){q.pop();}
    q.push(st);
    vis[st]=1;
    while(!q.empty())
    {
        int now=q.front();
        q.pop();
        for(int i=st;i<=ed;i++)
        {
            if(!vis[i]&&g[now][i])
            {
                 vis[i]=1;
                 pre[i]=now;
                 if(i==ed)//如果此次bfs能从s到t,返回1
                  return 1;
                  q.push(i);
            }
        }
    }
    return 0;
}
int EK()
{
    int v,d,maxflow=0;
    while(bfs())
    {
        v=ed;
        d=inf;
        while(v!=st)
        {
           d=min(d,g[pre[v]][v]);
           v=pre[v];
        }
        maxflow+=d;
        v=ed;
        while(v!=st)
        {
            g[pre[v]][v]-=d;//每次找到一条路,这条路每条正向边容量减少d
            g[v][pre[v]]+=d;//反向边容量减少d
            v=pre[v];
        }
    }
    return maxflow;
}

dinic:

 

  1. 用BFS建立分层图  注意:分层图是以当前图为基础建立的,所以要重复建立分层图,(分层图:从源点到当前点的最近距离,可以存储到一个数组中,距离源点相等的点dis[]相等)
  2. 用DFS的方法寻找一条由源点到汇点的路径,获得这条路径的流量I 根据这条路径修改整个图,将所经之处正向边流量减少I,反向边流量增加I,注意I是非负数    DFS的下一个节点的Dis(距源点的距离)要比自己的Dis大1

重复步骤2,直到DFS找不到新的路径时,重复步骤1

 

 

而层次图作用:

1、在DFS增广时,当且仅当下一个点的层次是当前点的下一层才进入下一个点;

2、是用来判断源点到汇点是否还有流量可以流。如果汇点已经不再层次图上了,说明没有多余的流量可以从源点流到汇点了,   这个时候就可以结束搜索而输出答案了。

 

int g[maxn][maxn];
int dis[maxn];
int n,m,k,mm;//mm是汇点
int bfs()
{
    memset(dis,0,sizeof(dis));
    queue<int >q;
    while(!q.empty()){q.pop();}
    q.push(1);
    dis[1]=1;
    while(!q.empty())
    {
        int now=q.front();
        q.pop();
        for(int i=1;i<=mm;i++)
        {
            if(!dis[i]&&g[now][i]>0)
            {
                dis[i]=dis[now]+1;
                q.push(i);
            }
        }
    }
    if(dis[mm]) return 1;
    return 0;
}
int dfs(int s,int u)
{
    if(s==mm) return u;
    int tmp=u;
    for(int i=1;i<=mm&&tmp;i++)
    {
        if(dis[i]==dis[s]+1&&g[s][i]>0)
        {
            int t=dfs(i,min(g[s][i],tmp));
            g[s][i]-=t;
            g[i][s]+=t;
            tmp-=t;;//从源点到s的瓶颈也会更小(减少t )
        }
    }
    return u-tmp;
}
int dinic()
{
    int t=0,ans=0;
    while(bfs())
    {
        while(t=dfs(1,inf))
            ans+=t;
    }
    return ans;
}

dinic邻接表做法:

 

///每组数据  first初始化为-1;cnt起始值设为1;
int dis[maxn];
char s[maxn];
int n,f,c,ed;
struct node
{
    int v;
    int cap;
    int next;
};
int first[maxn];
node g[M];
int cnt;
void addedge(int u,int v,int cap)
{
    g[cnt].v=v;
    g[cnt].cap=cap;
    g[cnt].next=first[u];
    first[u]=cnt++;
    g[cnt].v=u;
    g[cnt].cap=0;
    g[cnt].next=first[v];
    first[v]=cnt++;
}

int bfs()
{
    memset(dis,0,sizeof(dis));
    queue<int >q;
    while(!q.empty()){q.pop();}
    q.push(0);
    dis[0]=1;
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int e=first[u];~e;e=g[e].next)
        {
            int v=g[e].v;
            int cap=g[e].cap;
            if(!dis[v]&&cap>0)
            {
                dis[v]=dis[u]+1;
                q.push(v);
            }
        }
    }
    if(dis[ed]) return 1;
    return 0;
}
int dfs(int u,int flow)
{
    if(u==ed) return flow;
    int t;
    //int tmp=u;
    for(int e=first[u];~e;e=g[e].next)
    {
        int v=g[e].v;
        int cap=g[e].cap;
       if(dis[v]==dis[u]+1&&cap>0&&(t=dfs(v,min(cap,flow))))
        {
            g[e].cap-=t;
            g[e^1].cap+=t;
            return t;
            //tmp-=t;
        }
    }
    dis[u]=-1;
     return 0;
}
int Ans()
{
    int ans=0,t=0;
    while(bfs())
    {
        while(t=dfs(0,inf))
            ans+=t;
    }
    return ans;
}

 

 

 

 

 

 

 

 

 

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值