[模板] - 网络流 - Dinic & 当前弧优化

模板 专栏收录该内容
19 篇文章 0 订阅

函数:

邻接表部分:

const int INF = 0x3f3f3f3f, maxn = 157;
int N, NP, NC, M;
struct E {
    int u, v, flow;
    E(int u = 0, int v = 0, int flow = 0): u(u), v(v), flow(flow) {}
} edg[maxn * maxn];

int cnt_edg, S, T;
vector<int> edge[maxn]; // 边集
int dis[maxn];//距源点距离,分层图
int current[maxn];//当前弧

void addedg(int u, int v, int flow) {
    edge[u].push_back(cnt_edg);
    edg[cnt_edg++] = E(u, v, flow); // 正向边
    edge[v].push_back(cnt_edg);
    edg[cnt_edg++] = E(v, u, 0); // 反向边容量为0
    // 正向边下标通过异或就得到反向边下标, 2 ^ 1 == 3 ; 3 ^ 1 == 2
}

BFS建立分层图:

bool bfs() {
    queue<int> q;
    q.push(S);
    memset(dis, -1, sizeof(dis));
    dis[S] = 0;
    while (!q.empty()) {
        int index = q.front();
        q.pop();
        int sz = int(edge[index].size());
        for (int i = 0; i < sz; i++) {
            E &e = edg[edge[index][i]];
            if (e.flow > 0) {
                if (dis[e.v] < 0) {
                    dis[e.v] = dis[index] + 1;
                    q.push(e.v);
                }
            }
        }
    }
    return bool(~dis[T]); // 返回是否能够到达汇点
}

DFS增广 & 当前弧优化:

int dfs(int index, int maxflow) {
    if (index == T)
        return maxflow;
    // i = current[index] 当前弧优化
    int sz = int(edge[index].size());
    for (int i = current[index], number; number = edge[index][i], i < sz; i++) {
        current[index] = i;
        E &e = edg[number];
        if (dis[e.v] == dis[index] + 1 && e.flow > 0) {
            int flow = dfs(e.v, min(maxflow, e.flow));
            if (flow != 0) {
                e.flow -= flow; // 正向边流量降低
                edg[number ^ 1].flow += flow; // 反向边流量增加
                return flow;
            }
        }
    }
    return 0; // 找不到增广路 退出
}

Dinic运算主体:

int dinic() {
    int ans = 0;
    while (bfs()) {// 建立分层图
        int flow;
        memset(current, 0, sizeof(current)); // BFS后应当清空当前弧数组
        while (bool(flow = dfs(S, INF))) // 一次BFS可以进行多次增广
            ans += flow;
    }
    return ans;
}

其它:

  其实当前弧优化就只是在增广的过程中添加一个current[]数组记录下一次该走的边以避免重复,虽然是个很细微的部分但是在实际遍历当中的优化是十分巨大的。

  然后就是在每次分层完之后记得对current[]进行初始化。


汇总:

const int INF = 0x3f3f3f3f, maxn = 157;
int N, NP, NC, M;
struct E {
    int u, v, flow;
    E(int u = 0, int v = 0, int flow = 0): u(u), v(v), flow(flow) {}
} edg[maxn * maxn];

int cnt_edg, S, T;
vector<int> edge[maxn]; // 边集
int dis[maxn];//距源点距离,分层图
int current[maxn];//当前弧

void addedg(int u, int v, int flow) {
    edge[u].push_back(cnt_edg);
    edg[cnt_edg++] = E(u, v, flow); // 正向边
    edge[v].push_back(cnt_edg);
    edg[cnt_edg++] = E(v, u, 0); // 反向边容量为0
    // 正向边下标通过异或就得到反向边下标, 2 ^ 1 == 3 ; 3 ^ 1 == 2
}

bool bfs() {
    queue<int> q;
    q.push(S);
    memset(dis, -1, sizeof(dis));
    dis[S] = 0;
    while (!q.empty()) {
        int index = q.front();
        q.pop();
        int sz = int(edge[index].size());
        for (int i = 0; i < sz; i++) {
            E &e = edg[edge[index][i]];
            if (e.flow > 0) {
                if (dis[e.v] < 0) {
                    dis[e.v] = dis[index] + 1;
                    q.push(e.v);
                }
            }
        }
    }
    return bool(~dis[T]); // 返回是否能够到达汇点
}

int dfs(int index, int maxflow) {
    if (index == T)
        return maxflow;
    // i = current[index] 当前弧优化
    int sz = int(edge[index].size());
    for (int i = current[index], number; number = edge[index][i], i < sz; i++) {
        current[index] = i;
        E &e = edg[number];
        if (dis[e.v] == dis[index] + 1 && e.flow > 0) {
            int flow = dfs(e.v, min(maxflow, e.flow));
            if (flow != 0) {
                e.flow -= flow; // 正向边流量降低
                edg[number ^ 1].flow += flow; // 反向边流量增加
                return flow;
            }
        }
    }
    return 0; // 找不到增广路 退出
}

int dinic() {
    int ans = 0;
    while (bfs()) {// 建立分层图
        int flow;
        memset(current, 0, sizeof(current)); // BFS后应当清空当前弧数组
        while (bool(flow = dfs(S, INF))) // 一次BFS可以进行多次增广
            ans += flow;
    }
    return ans;
}
  • 4
    点赞
  • 5
    评论
  • 1
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值