zoukankan      html  css  js  c++  java
  • 图论五:网络流

    参考文章一:https://blog.csdn.net/txl199106/article/details/64441994

    参考文章二:https://blog.csdn.net/ergedathouder/article/details/55001645

    一、基本概念

    1、源点:只进不出的点,通常称为S

    2、汇点:只出不进的点,通常称为T

    3、容量&流量:容量是每条边上最大能经过的值,流量是当前经过边的值。(流量一定小于容量)

    容量用C表示,流量用F表示。

    4、三个基本性质:

    (1)每条边的流量小于容量,即C<F

    (2)流量守恒:Σ F<v,x> = Σ F<x,u>

    (3)斜对称性:F<x,y> = - F<y,x>

    5、容量网络&流量网络&残留网络

    残留网路 = 容量网络 - 流量网络

    6、割集:(可以分为点割集和边割集)

    就是去掉这一部分图不连通的部分。

    (最小割集是能成为割集的最小集合)

    7、最大流最小割定理

    (1)任意一个流都小于一个割;

    (2)将满流边作为一个割,就构造出和最大流相等的割;

    (3)最大流等于最小割

    设相等的流和割分别为Fm,Cm,

    则有:F<=Fm=Cm<=C(C,F为任意的)。

    三个等价条件:

    对于一个图G=(V,E)有源点s和汇点t

    (1)F是图G最大流

    (2)残留网络GF不存在增广路径,

    (3)对于G的一个割(S,T),存在f=C(S,T)。

    二、算法实现

     1、增广路径算法(EK算法)

    (1)算法功能:求增广路径

    (2)算法思想:bfs

    (3)算法流程:

    首先建立邻接表,存储每条边的数据,pre数组记录路径,vis数组标记路径,接着对数据进行初始化;

    其次,bfs遍历寻找最小边,然后更新整个图,建立反向边,并求和,更新ans的值,之后一直重复此操作。

    最后,如果bfs遍历时找不到最后的节点,说明图中没有增广路径,算法结束。

    代码:

    (1)邻接数组实现

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    using namespace std;
    const int maxn = 1200;
    const int INF = 0x3fff;
    int pre[maxn],p[maxn],vis[maxn],edge[maxn][maxn],m,n;
    int MIN(int x,int y){ return x<y?x:y; }
    void Init() { memset(edge,0,sizeof(edge)); } 
    int EK(int st,int ed) 
    {
        int ans=0,j,i,fg,mi;
        while(1)
        {
            fg=0;
            queue <int> q;
            q.push(st);
            memset(vis,0,sizeof(vis)); 
            memset(pre,0,sizeof(pre));
            p[st]=INF;
            while(!q.empty()) //寻找增广路径 
            {
                int top=q.front();
                q.pop();
                for(i=1;i<=n;i++)
                if(edge[top][i]&&vis[i]==0)
                {
                    vis[i]=1;
                    pre[i]=top; //记录增广路径 
                    if(i==ed){ fg=1;break; }
                    q.push(i);
                }
            }
            if(fg==0) return ans;
            mi=INF;
            for(i=ed;i!=st;i=pre[i]) mi=MIN(mi,edge[pre[i]][i]); //寻找最小值 
            for(i=ed;i!=st;i=pre[i]) edge[pre[i]][i]-=mi,edge[i][pre[i]]+=mi; //更新路径 
            ans+=mi;
        }
    } 
    int main(void)
    {
        int x,y,z;
        cin>>n>>m;
        Init();
        for(int i=1;i<=m;i++) cin>>x>>y>>z,edge[x][y]+=z;
        cout<<EK(1,n)<<endl;
        return 0;
    } 
    View Code

    (2)邻接表实现

    #include<iostream>
    #include<cstdio>
    #include<queue>
    #include<cstring>
    using namespace std;
    const int maxn = 1200;
    const int INF = 0x3fff;
    struct Edge{
        int v,next,w; //v表示边的终点,next表示下一个边的编号 
    }edge[maxn];
    struct Vertex{
        int v,id; //v表示下一个点,id表示当前的点 
    }pre[maxn];
    int n,m,cnt;
    int vis[maxn],head[maxn];
    int MIN(int x,int y){ return x<y?x:y; }
    void Init() //初始化 
    {
        cnt=0;
        memset(edge,0,sizeof(edge));
        memset(head,-1,sizeof(head));
    }
    void add(int u,int v,int w) //添加新边 
    {
        edge[cnt].v=v;
        edge[cnt].w=w;
        edge[cnt].next=head[u];
        head[u]=cnt++;
    }
    int EK(int st,int ed) //EK算法具体实现 
    {
        int i,j,fg,tmp,ans=0,mi;
        while(1)
        {
            fg=0;st=1;ed=n;
            memset(vis,0,sizeof(vis));
            memset(pre,-1,sizeof(pre));
            pre[st].v=st;vis[st]=1;
            queue <int> q;
            q.push(st);
            while(!q.empty())
            {
                int top=q.front();
                q.pop();
                for(i=head[top];i+1;i=edge[i].next)
                {
                    int v=edge[i].v;
                    if(vis[v]==0&&edge[i].w)
                    {
                        vis[v]=1;
                        pre[v].v=top;
                        pre[v].id=i;
                        if(v==ed) { fg=1;break; }
                        q.push(v);
                    }
                }
            }
            if(fg==0) return ans;
            mi=INF;
            for(i=ed;i!=st;i=pre[i].v) mi=MIN(mi,edge[pre[i].id].w); //正向路径更新 
            for(i=ed;i!=st;i=pre[i].v) edge[pre[i].id].w-=mi,edge[pre[i].id^1].w+=mi; //a^1表示取反,即pre[i].id的反向路径更新 
            ans+=mi;
        }
    }
    int main(void)
    {
        int x,y,z;
        cin>>n>>m;
        Init();
        for(int i=1;i<=m;i++)
        {
            cin>>x>>y>>z;
            add(x,y,z);add(y,x,0);
        }
        cout<<EK(1,n)<<endl;
        return 0;
    } 
    
    /*
    4 5
    1 2 2
    1 4 2
    2 4 3
    2 3 4
    4 3 4
    */
    View Code

    2、dinic算法

    算法流程:

    (1)初始化,计算出剩余图

    (2)根据剩余图计算层次图,若汇点不在层次图中,算法结束

    (3)层次图内用dfs计算增广路径

    (4)返回(2)

    代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    using namespace std;
    const int maxn = 1200;
    const int INF = 0x3ffffff;
    struct Node{
        int v,c,next;
    }edge[maxn*maxn<<1];
    int level[maxn],p[maxn],n,m,S,T,cnt;
    void Init()
    {
        memset(p,-1,sizeof(p));
        cnt=0;S=1;T=n;
    }
    int MIN(int x,int y)
    {
        return x<y?x:y;
    }
    void Insert(int u,int v,int c)
    {
        edge[cnt].v=v;
        edge[cnt].c=c;
        edge[cnt].next=p[u];
        p[u]=cnt++;
    }
    void addedge(int u,int v,int c)
    {
        Insert(u,v,c); 
        Insert(v,u,0);
    }
    bool bfs()
    {
        memset(level,-1,sizeof(level));
        level[S]=0;
        queue <int> q;
        q.push(S);
        while(!q.empty())
        {
            int u=q.front();
            q.pop();
            for(int i=p[u];i!=-1;i=edge[i].next)
            {
                int v=edge[i].v;
                if(edge[i].c>0&&level[v]==-1)
                {
                    level[v]=level[u]+1;
                    q.push(v);
                }
            }
        }
        return level[T]!=-1;
    }
    int dfs(int u,int cp)
    {
        if(u==T) return cp;
        int res=0;
        for(int i=p[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].v;
            if(level[u]+1==level[v]&&edge[i].c>0)
            {
                int t=dfs(v,MIN(cp,edge[i].c));
                cp-=t;
                res+=t;
                edge[i].c-=t;
                edge[i^1].c+=t;
                if(cp==0) break;
            }
        }
        if(res==0) level[u]=-1;
        return res;
    }
    int dinic()
    {
        int sum=0,tmp;
        while(bfs())
        sum+=dfs(S,INF);
        return sum;
    }
    int main(void)
    {
        int i,x,y,z;
        scanf("%d%d",&n,&m);
        Init();
        for(i=1;i<=m;i++)
        {
            scanf("%d%d%d",&x,&y,&z);
            addedge(x,y,z);
        }
        printf("%d
    ",dinic());
        return 0;
    }
    /*
    4 5
    1 2 2
    1 4 2
    2 4 3
    2 3 4
    4 3 4
    */
    View Code
  • 相关阅读:
    HDU 1102 Constructing Roads
    HDU 1285 确定比赛名次。
    最小生成树 HDU 各种畅通工程的题,prim和kru的模板题
    HDU Jungle Roads 1301 最小生成树、
    并查集小结(转)
    HDU hdu 2094 产生冠军 拓扑排序 判定环
    模运算(转)
    拓扑排序(主要是确定环和加法) HDU 2647 Reward
    HDU 1372 Knight Moves 简单BFS
    用计算机模型浅析人与人之间沟通方式 (一)如何谈话
  • 原文地址:https://www.cnblogs.com/2018zxy/p/10126716.html
Copyright © 2011-2022 走看看