dinic算法模板

用于计算最大流,带有当前弧优化
下面是对算法的简明概述:
首先dinic算法属于增广路算法,通过不断寻找从源点到汇点的增广路来实现扩流,但想较之Ford-Fulkerson算法来说,dinic在寻找增广路之前,将原图进行分层处理,即 以源点为深度为零的点,不断向下按照深度将原图的点标记;并且永远寻找最短的增广路来进行优化。因为最短的增广路在增广的过程中永远不会变短。
之后是代码实现:

  • 首先是边的存储结构 struct Edge
  • [1] to 表示边的终点
  • [2] cap表示这条边所代表的流量
  • [3] rev表示反向边在终点to邻接表中的位置,即用于指向反向边adj[to][rev]

接着是函数的说明

  • addEdge(int star,int finish,int cap)
    在图中建立一条边以及其反向边

  • dinic()
    dinic算法主体。首先bfs对原图分层,记录每个节点深度,之后dfs寻找图中最短增广路扩流。如果在现有图中,找不到增广路则重新对图分层,直到分层后不能到达汇点,即汇点没有深度标记,结束程序,返回最大流

  • bfs()
    从源点开始对原图进行搜索,标记每个点的最小深度(选取不同路径对应不同的深度,在这里选取每个点在搜索过程中第一次出现时,所对应的深度,也就是这个点的对源点的最小深度)。

  • dfs(int star,int finish,int f)
    在分层图中寻找最短的增广路,这就要求对于当前节点 star 到下一个节点的深度有如下的要求

     	level[star] = level[to] + 1
    

当前弧优化为 避免在寻找增广路的过程中对一条已知没用的边反复搜索。
在同一分层图中,可能会重复多次dfs。假设 在一次搜索过程中,到达节点 v ,并选择了边 adj[v][k] 。之后在下一次搜索中,有到达了节点 v ,那么我们可以根据上一次到达这个节点的搜索结果知道,对于任意的 i<k ,边adj[v][i] 都不能使用了,因此我们只需要从 k 开始搜索即可。而当图重新分层后,就需要把这些记录置零,重新记录。
在dfs中的改动

for(int &i=cur[star];i<adj[star].size();i++)

在dinic主体中改动

memset(cur,0,sizeof(cur));
while(d=dfs(starNode,finishNode,INF),d>0)
	sum+=d;

完整代码如下:


#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
#include <string>

#define MAXN
#define starNode 0
#define finishNode
#define INF 0x3f3f3f3f
#define ll long long
#define re return
#define getLen(name,index) name[index].size()
#define mem(a,b) memset(a,b,sizeof(a));

#define rep(index,star,finish) for(register int index=star;index<finish;index++)
#define drep(index,finish,star) for(register int index=finish;index>=star;index--)

using namespace std;
struct Edge{
    int to;
    int cap;
    int rev;
};

int cur[MAXN];
int level[MAXN];
vector<Edge> adj[MAXN];
inline void addEdge(int star,int finish,int cap);
int dinic();
void bfs();
int dfs(int star,int finish,int f);
int main(){
    ios::sync_with_stdio(false);

    re 0;
}
inline void addEdge(int star,int finish,int cap){
    adj[star].push_back((Edge){finish,cap,getLen(adj,finish)});
    adj[finish].push_back((Edge){star,0,getLen(adj,star)-1});
}
int dinic(){
    int sum=0,d;

    while(true){
        bfs();
        if(level[finishNode]<0)
            break;

        mem(cur,0);
        while(d=dfs(starNode,finishNode,INF),d>0){
            sum+=d;
        }
    }

    re sum;
}
void bfs(){
    mem(level,-1);
    level[starNode]=0;

    queue<int> Q;
    Q.push(starNode);
    while(!Q.empty()){
        int now=Q.front();
        Q.pop();

        int dep=level[now];
        rep(i,0,getLen(adj,now)){
            Edge &e=adj[now][i];

            if(e.cap>0 && level[e.to]<0){
                level[e.to]=dep+1;
                Q.push(e.to);
            }
        }
    }
}
int dfs(int star,int finish,int f){
    if(star==finish)
        re f;

    for(int &i=cur[star];i<getLen(adj,star);i++){
        Edge &e=adj[star][i];

        if(e.cap>0 && level[e.to]==level[star]+1){
            int d=dfs(e.to,finish,min(f,e.cap));
            if(d>0){
                e.cap-=d;
                adj[e.to][e.rev].cap+=d;
                re d;
            }
        }
    }
    re -1;
}



  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值