网络流最大流入门(Dinic算法)模板

2021-01-15 00:15

阅读:651

标签:set   class   文章   using   元组   ace   重复   ddn   大致   

前言

本来先搞计算几何再搞网络流的,但是**总是发网络流的题,然后天天被信息组巨佬爆踩,所以先学一下最基本的Dinic算法吧。我也只是大致理解了流程,其实不懂也没事,只要会堆代码就好了(QAQ),所以下面只有教你如何堆代码啦(……)。

基本概念

图和收发点

一个图是由点集V={vi}和V中元素的无序对的一个集合E={ek}所构成的二元组,记为G=(V,E),V中的元素vi叫做顶点,E中的元素ek,叫做边。
仅有一个入次为0的点vs称为发点(源),一个出次为0的点vt称为收点(汇),其余点为中间点,这样的网络G称为容量网络,常记做G=(V,E,C)。

容量和流量

技术图片
设有向连通图G=(V,E),G的每条边(vi,vj)上的非负数cij称为边的容量。对任一G中的边(vi,vj)有流量fij,称集合f={fij}为网络G上的一个流。右图即为一个有向连通图,括号中第一个数字代表容量,第二个数字代表流量。

可行流

称满足下列两个条件的流为可行流:
1.容量限制条件:对G中的每条边(vi,vj),有0≤fij≤cij;即每条边上的流量非负而且最大也只能达到容量的限制。
2.平衡条件:对中间点vi,有
技术图片
 ,即物资的输入量和输出量相等。
对发、收点vs,vt,,有
技术图片
 ,fij为网络流的总流量。
一个流f={fij},当fij=cij,则称流f对边(vi,vj)是饱和的,否则称f对(vi,vj)不饱和。
 

用途

当然就是求有向图中指定的某点到另一点的最大可行流(自己归纳的),解决这一问题的最好的算法就是Dinic了,下面给出具体流程。

算法流程

用n表示点数,m表示边数,st表示要求的起点,ed表示要求的终点(不专业术语,不要模仿呐)

首先存边

int head[],tot;
struct edge{int v,w,nxt;}e[];
void addn(int u,int v,int w){ 
    e[++tot]=(edge){v,w,head[u]};
    head[u]=tot;
}
//存边:u->v(w)和v->u(0)
addn(u,v,w);
addn(v,u,0);

然后码出每次dfs前需要的bfs建图

用cur[ ]代替变化的head[ ],dis[ ]表示到每个点到st的距离(每隔一条边距离就是1)

int bfs(int st,int ed)
{
    //bfs建图 
    queueint>que;
    memset(dis,-1,sizeof(dis));
    dis[st]=0;
    que.push(st);
    while(!que.empty())
    {
        int x=que.front();
        que.pop();
        for(int i=head[x];i;i=e[i].nxt)
        {
            int now=e[i].v;
            if(dis[now]==-1&&e[i].w)
            {
                que.push(now);
                dis[now]=dis[x]+1;
            }
        }
    }
    return dis[ed]!=-1;
}

接着码dfs查询

int dfs(int x,int t,int maxflow)
{
    if(x==t) return maxflow;
    int ans=0;
    for(int i=cur[x];i;i=e[i].nxt)
    {
        int now=e[i].v;
        if(dis[now]!=dis[x]+1||!e[i].w||ans>=maxflow) continue;
        cur[x]=i;
        int f=dfs(now,t,min(e[i].w,maxflow-ans));
        e[i].w-=f;
        if(i&1) e[i+1].w+=f;
        else e[i-1].w+=f;
        ans+=f;
    }
    if(!ans) dis[x]=-1;
    return ans;
}

然后大致流程就出来了,不断重复:bfs建图,dfs查询改值,直到不能再进行

现在放出全部代码

//dinic板子 
#include
#include
#include
#include
#include#define INF 0x3f3f3f3f
using namespace std;
const int N=10000,M=100000;
int n,m,tot,ss,dd,head[N+3];

struct edge{int v,w,nxt;}e[M*2+3];

int dis[N+3],cur[N+3];

void addn(int u,int v,int w){e[++tot]=(edge){v,w,head[u]};head[u]=tot;}

int bfs(int st,int ed)
{
    //bfs建图 
    queueint>que;
    memset(dis,-1,sizeof(dis));
    dis[st]=0;
    que.push(st);
    while(!que.empty())
    {
        int x=que.front();
        que.pop();
        for(int i=head[x];i;i=e[i].nxt)
        {
            int now=e[i].v;
            if(dis[now]==-1&&e[i].w)
            {
                que.push(now);
                dis[now]=dis[x]+1;
            }
        }
    }
    return dis[ed]!=-1;
}

int dfs(int x,int t,int maxflow)
{
    if(x==t) return maxflow;
    int ans=0;
    for(int i=cur[x];i;i=e[i].nxt)
    {
        int now=e[i].v;
        if(dis[now]!=dis[x]+1||!e[i].w||ans>=maxflow) continue;
        cur[x]=i;
        int f=dfs(now,t,min(e[i].w,maxflow-ans));
        e[i].w-=f;
        if(i&1) e[i+1].w+=f;
        else e[i-1].w+=f;
        ans+=f;
    }
    if(!ans) dis[x]=-1;
    return ans;
}

int Dinic(int st,int ed)
{
    int ans=0;
    while(bfs(st,ed))
    {
        memcpy(cur,head,sizeof(head));
        int k;
//        ans+=dfs(st,ed,INF); 
        while(k=dfs(st,ed,INF)) ans+=k;
    }
    return ans;
}


int main()
{
    scanf("%d %d %d %d",&n,&m,&ss,&dd);
    for(int i=1;i)
    {
        int u,v,w;
        scanf("%d %d %d",&u,&v,&w);
        addn(u,v,w);
        addn(v,u,0);
    }
    coutDinic(ss,dd);
    return 0;
}

注意:题目不同,m和n的大小也不同,记得要更改呀~

完结~~第一篇学术文章;撒花撒花??ヽ(°▽°)ノ?

 

网络流最大流入门(Dinic算法)模板

标签:set   class   文章   using   元组   ace   重复   ddn   大致   

原文地址:https://www.cnblogs.com/mm-puppy-lyt/p/mm-puppy-wllzdlDinic.html


评论


亲,登录后才可以留言!