立志成为饮水机!

网络流 之 dinic 算法

网络流指的是:网络流(network-flows)是一种类比水流的解决问题方法。(类似于水管群,有一个源点(水无限多),和一个汇点,最大流就代表这个点水管群(边集)每秒最大能送道汇点的水量)

这个怎么求,首先是枚举从原点能到汇点的路径,然后找到这个路径边权的最小值,这个路劲的每条边减去这个值,大概这个样子

(借用同校某大佬的图)

但是明显没给我反悔的机会嘛,万一我不想这样做,岂不没地方反悔了

~Point 1 反边~

这东西就是类似于给你一个反悔的机会的,因为反向图建过去的丑的一批的路径等价于每个单独路径

原理:正向边减去当前流量,反向边加上当前流量。

比如这张图中,有一个产地,要运货给销地(也就是6号点),问如何才能给销地运输最多货物(图片是学校ppt的)

显然这张图就没有刚才那张好办,因此我们加入反边这一个概念

问什么加入反边呢,先给你看这会在哪个加了反边的图(蓝色为反边边权)

(该运输方案表示:2吨产品沿有向路P1(1,2,4,6)运到销地;1吨产品沿有向路P2(1,2,5,6)运到销地;2吨产品沿有向路P3(1,3,5,6)运到销地。)

 现在我们又找到一条P4(1,2,3,4,6)的路径。

 

Level 1.P3376 【模板】网络最大流

就是一个裸题……

由于这个代码是第一次掌握,有些东西还不能完全化为自己的理解,故有些地方传承了教练给的代码的格式,这些东西我会在里面说明的

但是作为入门内容,还是打一下。我的注释应该会给的很清楚吧

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

using namespace std;

int n,m,st,ed,size,ans,ct[10086],head[10086],dep[10086],vis[10086];

struct edge{
    int next,to,dis,start;
}e[200860];

void addedge(int next,int to,int dis)
{
    e[size].to=to;  
    e[size].dis=dis; 
    e[size].next=head[next];
    e[size].start=next;  //这个貌似没什么用 
    head[next]=size;
    size++;  //还有另一种写法,详见下面addedge 
}

void addedge(int next,int to,int dis)
{
    e[++size].dis=dis;  //这种写法可以先令size=1,这样 边就从2开始存了,也能满足异或1是反向边 
    e[size].to=to;   //或者令size =-1 和就和上面写法差不多 
    e[size].next=head[next];
    head[next]=size;
 } 

int bfs()
{
    queue <int> q;
    memset(dep,0,sizeof(dep));
    memset(vis,0,sizeof(vis));
    q.push(st);
    vis[st]=1;
    while(!q.empty())
    {
        int t=q.front();
        q.pop();
        int i,j,k;
        for(i=head[t];i!=-1;i=e[i].next)
        {
            j=e[i].to;
            k=e[i].dis;
            if(k>0&&!dep[j]&&!vis[j])  //dep类似于图的深度,把网络流看作水流,防止回流 
            {
                dep[j]=dep[t]+1;
                q.push(j);
                vis[j]=1;
            }
        }
    }
    return vis[ed];  //如果终点还能被增广就就返回1 
}

int dfs(int t,int nowwater)
{
    if(t==ed||!nowwater) return nowwater;  //1.到终点返回答案,2.走不通返回0 
    int tot=0;
    for(int &i=ct[t];i!=-1;i=e[i].next)  //当前弧优化,防止无用寻找 
    {
        int j=e[i].to;
        int &k=e[i].dis;  //暂时不知道这个&有什么意思,但是去掉也可以过 
        if(k>0&&dep[j]==dep[t]+1)
        {
            int f=dfs(j,min(k,nowwater-tot));
            e[i].dis-=f;
            e[i^1].dis+=f;  //异或1让二进制最后一位变化 比如1001001 ^1 ==1001000 刚好指向另一条边 
            tot+=f;
            if(nowwater==tot) return tot;
        }
    }
    return tot; //当前能流到终点的最大水量 
}

void dinic()
{
    while(bfs())
    {
        for(int i=1;i<=n;i++)
        {
            ct[i]=head[i];
        }
        ans+=dfs(st,0x3f3f3f3f);  //初始值一定设为最大值 
    }
}

int main()
{
    int i,j;
    scanf("%d %d %d %d",&n,&m,&st,&ed);
    memset(head,-1,sizeof(head));
    for(i=1;i<=m;i++)
    {
        int t1,t2,t3;
        scanf("%d %d %d",&t1,&t2,&t3);  //建边建正向边和反向边 
        addedge(t1,t2,t3);
        addedge(t2,t1,0);
    }
    dinic();
    printf("%d",ans);
    return 0;
}

 时间复杂度O(可以过)

posted @ 2019-07-09 14:14  寒冰大大  阅读(437)  评论(0编辑  收藏  举报