P3376 网络流-最大流模板题(Dinic+当前弧优化)

(点击此处查看原题)

Dinic算法

Dinic算法相对于EK算法,主要区别在于Dinic算法对图实现了分层,使得我们可以用一次bfs,一次dfs使得多条增广路得到增广

普通的Dinic算法已经可以处理绝大多数最大流(最小割)的题目了,但是总是有些题目会卡住普通的Dinic算法,此时我们就需要用到当前弧优化了

当前弧优化简述

不要小看当前弧优化,这个优化效果可是很明显的,就这个例题来说,我用普通的Dinic算法用时约1.7s,而使用了当前弧优化的Dinic算法后,只用了176ms,由此可以看出这个优化的强大

当前弧优化的核心思想为:避免遍历已经满流的边,总所周知,已经满流的边已经再构成增广路,而普通Dinic算法中,我们总是遍历所有的边,后判断这条边是否满流,这样相当于白白消耗时间

回想一下我们普通Dinic算法中将增广路增广的方法:我们总是尽可能地将某一条边的容量完全利用,因为我们建立了反边,可以“反悔”,因此我们可以完全利用这条边的容量;而对于被完全利用的边,因为这条边已经满流了,之后的增广路中不会用到这条边,那么我们就可以标记一下从每个点出发的第一条不满流边,下次从这个点开始求增广路的时候,就可以跳过之前已经满流的那些边,直接从不满流边开始遍历了

具体操作的话,我们用cur数组记录以每个点为起点的边当前可用的第一条不满流边,在dfs之前我们将head数组的值复制给cur数组,然后再dfs将增广路增广的时候,我们记录下以i为起点的第一条不满流边,即cur[i],因为我们从i继续向后增广的时候,只要流入流量flow_in用尽,当前枚举到的这条边之前的边都是满流的了,所以我们记录下最后一条遍历的边作为cur[i],下次遍历从这个点出发的边的时候,从cur[i]开始遍历即可。

代码区

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<string>
#include<fstream>
#include<vector>
#include<stack>
#include <map>
#include <iomanip>

#define bug cout << "**********" << endl
#define show(x, y) cout<<"["<<x<<","<<y<<"] "
#define LOCAL = 1;
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const ll mod = 1e6 + 3;
const int Max = 1e5 + 10;

struct Edge {
    int to, next, flow;    //flow记录这条边当前的边残量
}edge[Max << 1];

int n, m, s, t;
int head[Max], tot;
int dis[Max],cur[Max];

void init()
{
    memset(head, -1, sizeof(head));tot = 0;
}

void add(int u, int v, int flow)
{
    edge[tot].to = v;
    edge[tot].flow = flow;
    edge[tot].next = head[u];
    head[u] = tot++;
}

bool bfs() //判断图是否连通
{
    queue<int>q;
    memset(dis, -1, sizeof(dis));
    dis[s] = 0;
    q.push(s);
    while (!q.empty())
    {
        int u = q.front();q.pop();
        for (int i = head[u]; i != -1; i = edge[i].next)
        {
            int v = edge[i].to;
            if (dis[v] == -1 && edge[i].flow > 0)        //可以借助边i到达新的结点
            {
                dis[v] = dis[u] + 1;                    //求顶点到源点的距离编号
                q.push(v);
            }
        }
    }
    return dis[t] != -1;                                //确认是否连通
}

int dfs(int u, int flow_in)
{
    if (u == t) return flow_in;
    int flow_out = 0;                                    //记录这一点实际流出的流量
    for (int i = cur[u]; i != -1;i = edge[i].next)
    {
        cur[u] = i;                                     //由于我们增广的时候都是尽量用尽这条边的容量为前提的,
                                                        // 那么在在用尽流入流量之前,我们使用的边,都已经满流了
                                                        //没有继续增广的可能了,所以抛弃了这些重复的边,从未满流的边开始遍历
        int v = edge[i].to;
        if (dis[v] == dis[u] + 1 && edge[i].flow > 0)
        {
            int flow_part = dfs(v, min(flow_in, edge[i].flow));
            if (flow_part == 0)continue;                //无法形成增广路
            flow_in -= flow_part;                        //流出了一部分,剩余可分配流入就减少了
            flow_out += flow_part;                        //记录这一点最大的流出

            edge[i].flow -= flow_part;
            edge[i ^ 1].flow += flow_part;                //减少增广路上边的容量,增加其反向边的容量
            if (flow_in == 0)
                break;
        }
    }
    return flow_out;
}

int max_flow()
{
    int sum = 0;
    while (bfs())
    {
        for(int i = 1;i <= n ;i ++)
            cur[i] = head[i];
        sum += dfs(s, inf);
    }
    return sum;
}

int main() {
#ifdef LOCAL
    //freopen("input.txt", "r", stdin);
    //freopen("output.txt", "w", stdout);
#endif
    while (scanf("%d%d%d%d", &n, &m, &s, &t) != EOF)
    {
        init();
        for (int i = 1, u, v, flow;i <= m; i++)
        {
            scanf("%d%d%d", &u, &v, &flow);
            add(u, v, flow);add(v, u, 0);
        }

        printf("%d\n", max_flow());
    }

    return 0;
}

原文地址:https://www.cnblogs.com/winter-bamboo/p/11385276.html

时间: 2024-08-08 20:50:14

P3376 网络流-最大流模板题(Dinic+当前弧优化)的相关文章

hdu 3549 Flow Problem(最大流模板题)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3549 Problem Description Network flow is a well-known difficult problem for ACMers. Given a graph, your task is to find out the maximum flow for the weighted directed graph. Input The first line of input

[ACM] hdu 3549 Flow Problem (最大流模板题)

Flow Problem Problem Description Network flow is a well-known difficult problem for ACMers. Given a graph, your task is to find out the maximum flow for the weighted directed graph. Input The first line of input contains an integer T, denoting the nu

POJ2135 最小费用最大流模板题

练练最小费用最大流 此外此题也是一经典图论题 题意:找出两条从s到t的不同的路径,距离最短. 要注意:这里是无向边,要变成两条有向边 #include <cstdio> #include <cstring> #define MAXN 1005 #define MAXM 10005 #define INF 0x3f3f3f3f struct Edge { int y,c,w,ne;//c容量 w费用 }e[MAXM*4]; int n,m,x,y,w; int s,t,Maxflow

【网络流#2】hdu 1533 最小费用最大流模板题

嗯~第一次写费用流题... 这道就是费用流的模板题,找不到更裸的题了 建图:每个m(Man)作为源点,每个H(House)作为汇点,各个源点与汇点分别连一条边,这条边的流量是1(因为每个源点只能走一条边到汇点),费用是 从源点走到汇点的步数,因为有多个源点与汇点,要建一个超级源点与超级汇点,超级源点与各个源点连一条流量为1,费用为0(要避免产生多余的费用)的边 按照这个图跑一发费用流即可 把代码挂上去,用的是前向星写的 1 #include<cstdio> 2 #include<cstr

POJ1273 Drainage Ditches 最大流模板题(dinic)

最大流的模板题 给出边数M,顶点数N 以及每条边的容量 求1到N的最大流 注意可以有重边 邻接矩阵模板: #include<iostream> #include<cstdio> #include<cstring> #define maxx 0x3f3f3f #define M 205 using namespace std; int arc[M][M]; //弧的剩余流量 int level[M]; int n; int min(int a,int b) { retur

[网络流]最大流模板

网络流最大流最小割 题目链接 就是一道点割最小割模板. 找了许久的模板,终于遇到了 先说边割 边割比较常见. 最大流 最大流等于最小割,我懒得证. 求最大流的思路就是每次尝试找一条从源点到汇点的通路,然后找到这条路上残余流量最小的流量,答案加上这个流量,这条通路上每条边的残余流量减去这个值,反向边加上这个值. 关于反向边,实际上是一个反悔机制.反向流多少,表示正向已经流了多少.也就是说,如果我们从反向边流了一些流量,就相当于从这条边退回了一部分流量. 点割 所谓点割,就是被割掉的不再是边,而是点

洛谷P3381——费用流模板题

嗯..随便刷了一道费用流的模板题....来练练手. #include<iostream> #include<cstdio> #include<cstring> using namespace std; int h[5210],d[5210],used[5210],que[100010],last[5210]; int k=1,INF=0x7fffffff,ans1=0,ans2=0; inline int read(){ int t=1,num=0; char c=ge

HDU-3549 最大流模板题

1.HDU-3549   Flow Problem 2.链接:http://acm.hdu.edu.cn/showproblem.php?pid=3549 3.总结:模板题,参考了 http://www.cnblogs.com/Lyush/archive/2011/08/08/2130660.html  ,里面有几种模板,没太看懂 题意:给定有向图,求第1到第n个点的最大流. #include<iostream> #include<cstring> #include<cmat

hdu4292 Food 最大流模板题

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4292 题意:水和饮料,建图跑最大流模板. 我用的是学长的模板,最然我还没有仔细理解,不过这都不重要直接贴就行了. 下面是AC代码,以后就当做最大流的模板来用了. 代码: #include<cstdio> #include<iostream> using namespace std; const int oo=1e9; const int mm=2e5+5; const int mn=1