zoukankan      html  css  js  c++  java
  • 网络流 小结(更新时间2015/8/8)更新中

    国家队集训队论文-网络流(下载链接)

    基础知识我就不再累述了,大家百度百科或找某大牛博客看看就好了

    下面是摘自某牛(http://www.cnblogs.com/neverforget/archive/2011/10/20/2210785.html)的一些总结:

    第一部分.最大流的算法

    下面步入与实际问题更加接近的算法实现部分,首先给出问题,给定一个流网络,求源到汇在单位时间内的最大流量。

    最简单而效率较好的算法 是基于增广路的算法,这类算法在王欣上大牛的论文中有详细介绍,但我仍然想谈谈我的想法,希望能起到抛砖引玉的作用。基于增广路的算法主要有两种:MPLA,Dinic,SAP.其中最简单的是MPLA,最实用最简洁也是最多人用的是Dinic,SAP的范围也很广,加上GAP优化后的效率也让人咋舌,这也是最近SAP大泛滥的原因吧!个人比较喜欢Dinic,数据变态就用最高标号预流推进,SAP用的比较少,当然,用什么算法还是看你自己的感觉吧。有些人认为增广路算法格式低效,于是想出了对于每个节点操作的算法,这类算法以预留推进为顶梁柱,MPM也勉强归入这一类吧。

     

    1.MPLA算法

    即最短路径增值算法,可以有一个简单的思想,每次都找一条从源到汇的路径来增广,直到不能增广为止,之中算法的正确性是可以保证的,但效率不尽如人意,有些时候,把事情格式化反而有益,这里的MPLA就是这样,它只在层次图中找增广路,构建出层次图之后,用BFS不断增广,直到当前层次图中不再有增广路,再重新构建层次图,如果汇点不在层次图内,则源汇不再连通,最大流已经求出,否则继续执行增广,如此反复,就可以求出最大流,在程序实现时层次图不用被构建出来,只需要BFS出各点的距离标号,找路径时判断对于f(u,v)是否有d[u]+1=d[v]即可。

    如果每建一次层次图成为一个阶段,则在最短路径增值算法中,最多有N个阶段,证明略过。

    因此在整个算法中,最多有N个阶段,每个阶段构建层次图的BFS时间复杂度为O(m),建N次,因此构建层次图的总时间为O(mn),而在增广过程中,每一次增广至少删除一条边,因此增广m次,加上修改流量的时间,每一阶段的增广时间为O(m*(m+n)),共有N个阶段,所以复杂度为O(n*m*(m+n))=O(nm^2),这也是该算法的时间复杂度。

     hdu 1532模板题 Drainage Ditches

    /**************************************************************
        Problem:
        User: youmi
        Language: C++
        Result: Accepted
        Time:
        Memory:
    ****************************************************************/
    //#pragma comment(linker, "/STACK:1024000000,1024000000")
    //#include<bits/stdc++.h>
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <map>
    #include <stack>
    #include <set>
    #include <sstream>
    #include <cmath>
    #include <queue>
    #include <string>
    #include <vector>
    #define zeros(a) memset(a,0,sizeof(a))
    #define ones(a) memset(a,-1,sizeof(a))
    #define sc(a) scanf("%d",&a)
    #define sc2(a,b) scanf("%d%d",&a,&b)
    #define sc3(a,b,c) scanf("%d%d%d",&a,&b,&c)
    #define scs(a) scanf("%s",a)
    #define sclld(a) scanf("%I64d",&a)
    #define pt(a) printf("%d
    ",a)
    #define ptlld(a) printf("%I64d
    ",a)
    #define rep0(i,n) for(int i=0;i<n;i++)
    #define rep1(i,n) for(int i=1;i<=n;i++)
    #define rep_1(i,n) for(int i=n;i>=1;i--)
    #define rep_0(i,n) for(int i=n-1;i>=0;i--)
    #define Max(a,b) (a)>(b)?(a):(b)
    #define Min(a,b) (a)<(b)?(a):(b)
    #define lson (step<<1)
    #define rson (lson+1)
    #define esp 1e-6
    #define oo 0x7fffffff
    #define TEST cout<<"*************************"<<endl
    
    using namespace std;
    typedef long long ll;
    
    int n,m;
    
    const int maxn=200+10;
    int dis[maxn];
    int pc[maxn][maxn];
    int bfs()
    {
        ones(dis);
        dis[1]=0;
        queue<int >q;
        q.push(1);
        while(!q.empty())
        {
            int u=q.front();
            q.pop();
            rep1(v,m)
            {
                if(dis[v]==-1&&pc[u][v])
                {
                    dis[v]=dis[u]+1;
                    q.push(v);
                }
            }
        }
        if(dis[m]>0)
            return 1;
        return 0;
    }
    int dfs(int u,int flow)
    {
        int temp;
        if(u==m)
            return flow;
        rep1(v,m)
        {
            if(pc[u][v]&&dis[v]==(dis[u]+1)&&(temp=dfs(v,Min(pc[u][v],flow))))
            {
                pc[u][v]-=temp;
                pc[v][u]+=temp;
                return temp;
            }
        }
        return 0;
    }
    int main()
    {
        //freopen("in.txt","r",stdin);
        while(~sc2(n,m))
        {
            zeros(pc);
            int u,v;
            int w;
            rep1(i,n)
            {
                sc2(u,v);
                sc(w);
                pc[u][v]+=w;
            }
            int flow,ans=0;
            while(bfs())
            {
                while((flow=dfs(1,oo))!=0)
                    ans+=flow;
            }
            pt(ans);
        }
        return 0;
    }
    View Code

    2.Dinic算法

    MPLA虽然简单,但经常会点超时,我们把增广过程中的BFS改成DFS,效率会有比较大的提高么?答案是肯定的,至此我们已经得到了Dinic的算法流程,只是将MPLA的增广改为DFS,就能写出那美妙的Dinic了,同样,分析一下时间,在DFS过程中,会有前进和后退两种情况,最多前进后退N次,而增广路最多找M次,再加上N个阶段,所以Dinic的复杂度就是O(mn^2),事实上,它也确实比MPLA快很多,简洁而比较高效,这也是许多OIER选择Dinic的理由了吧,毕竟,写它可能会节省出较长时间来完成其他题目.

     poj 1459 Power Network

    /**************************************************************
        Problem:
        User: youmi
        Language: C++
        Result: Accepted
        Time:
        Memory:
    ****************************************************************/
    //#pragma comment(linker, "/STACK:1024000000,1024000000")
    //#include<bits/stdc++.h>
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <map>
    #include <stack>
    #include <set>
    #include <sstream>
    #include <cmath>
    #include <queue>
    #include <string>
    #include <vector>
    #define zeros(a) memset(a,0,sizeof(a))
    #define ones(a) memset(a,-1,sizeof(a))
    #define sc(a) scanf("%d",&a)
    #define sc2(a,b) scanf("%d%d",&a,&b)
    #define sc3(a,b,c) scanf("%d%d%d",&a,&b,&c)
    #define scs(a) scanf("%s",a)
    #define sclld(a) scanf("%I64d",&a)
    #define pt(a) printf("%d
    ",a)
    #define ptlld(a) printf("%I64d
    ",a)
    #define rep0(i,n) for(int i=0;i<n;i++)
    #define rep1(i,n) for(int i=1;i<=n;i++)
    #define rep_1(i,n) for(int i=n;i>=1;i--)
    #define rep_0(i,n) for(int i=n-1;i>=0;i--)
    #define Max(a,b) (a)>(b)?(a):(b)
    #define Min(a,b) (a)<(b)?(a):(b)
    #define lson (step<<1)
    #define rson (lson+1)
    #define esp 1e-6
    #define oo 0x3fffffff
    #define TEST cout<<"*************************"<<endl
    
    using namespace std;
    typedef long long ll;
    
    int n,np,nc,m;
    
    const int maxn=200+10;
    int dis[maxn];
    int pc[maxn][maxn];
    int bfs()
    {
        ones(dis);
        dis[0]=0;
        queue<int >q;
        q.push(0);
        while(!q.empty())
        {
            int u=q.front();
            q.pop();
            rep1(v,n)
            {
                if(dis[v]==-1&&pc[u][v])
                {
                    dis[v]=dis[u]+1;
                    q.push(v);
                }
            }
        }
        if(dis[n]>0)
            return 1;
        return 0;
    }
    int dfs(int u,int flow)
    {
        int temp;
        if(u==n)
            return flow;
        rep1(v,n)
        {
            if(pc[u][v]&&dis[v]==(dis[u]+1)&&(temp=dfs(v,Min(pc[u][v],flow))))
            {
                pc[u][v]-=temp;
                pc[v][u]+=temp;
                return temp;
            }
        }
        return 0;
    }
    
    int main()
    {
        //freopen("in.txt","r",stdin);
        while(~scanf("%d%d%d%d",&n,&np,&nc,&m))
        {
            n++;
            int u,v,w;
            zeros(pc);
            rep1(i,m)
            {
                scanf(" (%d,%d)%d",&u,&v,&w);
                u++,v++;
                //printf("u->%d v->%d
    ",u,v);
                pc[u][v]=w;
            }
            rep1(i,np)
            {
                scanf(" (%d)%d",&v,&w);
                v++;
                //printf("v->%d w->%d
    ",v,w);
                pc[0][v]=w;
            }
            rep1(i,nc)
            {
                scanf(" (%d)%d",&u,&w);
                u++;
                pc[u][n]=w;
            }
            int flow,ans=0;
            while(bfs())
            {
                while((flow=dfs(0,oo))!=0)
                    ans+=flow;
            }
            pt(ans);
        }
        return 0;
    }
    View Code

    3.SAP算法

        SAP( shortest augment path )也是找最短路径来增广的算法,有这样一句话:SAP算法更易理解,实现更简单,效率更高,而也有测试表明,SAP加上重要的GAP优化后,效率仅次于最高标号预流推进算法,因此如果你想背一个模板,SAP是最佳选择。SAP在增广时充分的利用了以前的信息,当按照高度找不到增广路时,它会对节点重新标号,h[i]=min{h[j]}+1(c[i,j]>0),这也是SAP比较核心的思想,而根据这个我们可以发现,当高度出现间隙时,一定不会存在增广路了,算法已经可以结束,因此,这里引入间隙优化(GAP),即出现间隙时结束算法。

        在算法实现中,初始标号可以全部置为0,在增广过程中在逐渐提升高度,时间上可能会有常数的增加,但不改变渐进时间复杂度。同时为了简洁,SAP实现时用递归,代码不过80行左右。

    4.MPM算法

        这个算法我还没有实践过,因为它的实现过程比较繁琐,而且时间效率不高,是一个只具有理论价值的算法,这个算法每次都处理单独节点,记每个节点入流和与出流和的最小值作为thoughput(now)(定义在非源汇点),每次先从now向汇推大小为thoughput(now)的流量,在从点now向源点拉大小为thoughput(now)的流量,删除该节点,继续执行直到图中只剩下源汇。时间复杂度为O(n^3),但时间常数较大,时间效率不高。

    5.预留推进算法

        以上的算法中,基本上都需要从大体上来把握全局,而预留推进算法则是将每一个顶点看作了一个战场,分别对他们进行处理,在处理过程中,存在某些时间不满足流量收支平衡,所以对预先推出的流叫做预流,下面来看算法如何将预流变成最大流的。

        预留推进算法有两个主过程,push和relabel,即推进和重标号,它是在模拟水流的过程,一开始先让源的出弧全部饱和,之后随着时间的推移,不断改变顶点的高度,而又规定水流仅能从高处流向低处,所以在模拟过程中,最终会有水流入汇,而之前推出的多余的水则流回了源,那么我们每次处理的是什么节点呢?把当前节点内存有水的节点称为活跃节点,每次对活跃节点执行推流操作,直到该节点不再活跃,如果不能再推流而当前节点仍未活跃节点,就需要对它进行重新标号了,标号后再继续推流,如此重复,直到网络中不再存在活跃节点为止,这时源的流出量就是该网络的最大流。注意,对于活跃节点的定义,不包括源汇,否则你会死的很惨。

        朴素的预留推进的效率还过得去,最多进行nm次饱和推进和n^2m次不饱和推进,因此总的时间复杂度为O(mn^2)

        事实上,如同增广路算法引入层次图一样,定下一些规则,可以让预留推进算法有更好的时间效率,下面介绍相对而言比较好实现的FIFO预留推进算法,它用一个队列来保存活跃节点,每次从队首取出一个节点进行推进,对一个节点relabel之后把它加到队尾,如此执行,直到队列为空,这样一来,预留推进算法的时间复杂度降为O(n^3),实现的时候,可以加上同样的间隙优化,但注意,出现间隙时不要马上退出,将新标号的的高度置为n+1,继续执行程序,这样会让所有的剩水流回源,满足流量收支平衡,以便最后的统计工作。

     

    下面介绍最后一个,也是编程难度最大,时间表现不同凡响的算法,最高标号预流推进,它的思想是既然水是从高处向低处流的,那么如果从低处开始会做许多重复工作,不如从最高点开始流,留一次就解决问题。再直观一些,引用黑书上的话“让少数的节点聚集大量的盈余,然后通过对这些节点的检查把非饱和推进变成一串连续的饱和推进”。在程序现实现时,用一个表list来储存所有的活跃节点,其中list(h)存储高的为h的活跃节点,同时记录一个level,为最高标号,每次查找时依次从level,level-1……查找,直到找到节点为止,这时从表内删掉这个节点,对它进行Push,Relabel操作,直到该节点不再活跃,继续进行,直到表内不在存在活跃节点。

         它的复杂度为O(n^2*m^(1/2)),时间效率很优秀(当然,如果你刻意构造卡预留推进的数据,它比MPLA还慢也是有可能的)。

    小结:

    网络流的最大流算法种类繁多,时间效率编程复杂度也不尽相同,对于不同的流网络,选择相应的算法,需要在不断实践中摸索,这也是一个菜鸟到大牛的必经之路。在一般题目中,选用Dinic是一个不错的想法,但当我们发现网络特别稠密时,FIFO的预留推进算法就要派上用场了,而时间比较紧但题目数据弱,我们甚至可以采用搜索找增广路的算法。

     

     

     

    第三部分  最小费用最大流问题

     学习了网络流的最大流算法,一定有一种十分兴奋的感觉,那么,就让你借着这股兴奋劲儿,来学习这一章的最小费用流吧。

    最小费用流有两种经典的算法,一种是消圈算法,另一种则是最小费用路增广算法。

    第一种,消圈算法。如果在一个流网络中求出了一个最大流,但对于一条增广路上的某两个点之间有负权路,那么这个流一定不是最小费用最大流,因为我们可以让一部分流从这条最小费用路流过以减少费用,所以根据这个思想,可以先求出一个最大初始流,然后不断地通过负圈分流以减少费用,直到流网络中不存在负圈为止。

    消圈算法的时间复杂度上限为O(nm^2cw),其中c是最大流量,w为费用最大值,而按特定的顺序消圈的时间复杂度为O(nm^2logn)。这里的时间复杂度分析是按照用bellman-ford算法消圈得到的,用SPFA应该可以得到更优的实际运行时间。

    第二种,最小费用路增广算法。这里运用了贪心的思想,每次就直接去找s到t的最小费用路来增广,这样得到的结果一定是最小费用,实现较简单,时间复杂度O(mnv),v为最大流量。用SPFA效果极好,但鉴于SPFA的不确定性,有时为了保险,往往运用重新加权技术,具体实践请通过网络或其他途径获得。

    最小费用流的东西并不多,事实上是使用最短路径这种特殊的网络流解决了普遍的网络流问题,只要掌握好基础,程序不难写出。

    poj 2195 Going home

    /**************************************************************
        Problem:
        User: youmi
        Language: C++
        Result: Accepted
        Time:
        Memory:
    ****************************************************************/
    //#pragma comment(linker, "/STACK:1024000000,1024000000")
    //#include<bits/stdc++.h>
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <map>
    #include <stack>
    #include <set>
    #include <sstream>
    #include <cmath>
    #include <queue>
    #include <string>
    #include <vector>
    #define zeros(a) memset(a,0,sizeof(a))
    #define ones(a) memset(a,-1,sizeof(a))
    #define sc(a) scanf("%d",&a)
    #define sc2(a,b) scanf("%d%d",&a,&b)
    #define sc3(a,b,c) scanf("%d%d%d",&a,&b,&c)
    #define scs(a) scanf("%s",a)
    #define sclld(a) scanf("%I64d",&a)
    #define pt(a) printf("%d
    ",a)
    #define ptlld(a) printf("%I64d
    ",a)
    #define rep0(i,n) for(int i=0;i<n;i++)
    #define rep1(i,n) for(int i=1;i<=n;i++)
    #define rep_1(i,n) for(int i=n;i>=1;i--)
    #define rep_0(i,n) for(int i=n-1;i>=0;i--)
    #define Max(a,b) (a)>(b)?(a):(b)
    #define Min(a,b) (a)<(b)?(a):(b)
    #define lson (step<<1)
    #define rson (lson+1)
    #define esp 1e-6
    #define oo 0x3fffffff
    #define TEST cout<<"*************************"<<endl
    
    using namespace std;
    typedef long long ll;
    
    int n,m;
    
    const int maxn=310;
    char s[maxn][maxn];
    struct node
    {
        int x,y;
        node(int l,int r):x(l),y(r){}
    };
    vector<node>man,house;
    int pp,T;
    struct side
    {
        int u,v,w,res,c,next;
    }e[maxn*maxn];
    int head[maxn<<2];
    void init()
    {
        T=0;
        pp=0;
        man.clear();
        house.clear();
        ones(head);
    }
    void build(int u,int v,int w,int c)
    {
        e[T].u=u;
        e[T].v=v;
        e[T].w=w;
        e[T].c=c;
        e[T].res=T+1;
        e[T].next=head[u];
        head[u]=T++;
    
        e[T].u=v;
        e[T].v=u;
        e[T].w=0;
        e[T].c=-c;
        e[T].res=T-1;
        e[T].next=head[v];
        head[v]=T++;
    }
    int inq[maxn<<2];
    int dis[maxn<<2];
    int pre[maxn<<2];
    bool spfa(int st,int ed)
    {
        zeros(inq);
        for(int i=st;i<=ed;i++)
            dis[i]=oo;
        dis[st]=0;
        queue<int >q;
        q.push(st);
        while(!q.empty())
        {
            int u=q.front();
            q.pop();
            inq[u]=0;
            for(int i=head[u];~i;i=e[i].next)
            {
                int v=e[i].v;
                if(dis[v]>dis[u]+e[i].c&&e[i].w)
                {
                    dis[v]=dis[u]+e[i].c;
                    pre[v]=i;
                    if(!inq[v])
                    {
                        inq[v]=1;
                        q.push(v);
                    }
                }
            }
        }
        if(dis[ed]==oo)
            return false;
        return true;
    }
    int c,f;
    void min_cost_max_flow(int st,int ed)
    {
        c=0;
        int p;
        while(spfa(st,ed))
        {
            f=oo;
            for(int u=ed;u!=st;u=e[p].u)
            {
                p=pre[u];
                f=Min(f,e[p].w);
            }
            for(int u=ed;u!=st;u=e[p].u)
            {
                p=pre[u];
                e[p].w-=f;
                e[e[p].res].w+=f;
            }
            c+=dis[ed]*f;
        }
    }
    int main()
    {
        //freopen("in.txt","r",stdin);
        while(~sc2(n,m)&&n+m)
        {
            init();
            rep1(i,n)
                scs(s[i]+1);
            rep1(i,n)
                rep1(j,m)
                {
                    if(s[i][j]=='m')
                    {
                        pp++;
                        man.push_back(node(i,j));
                    }
                    if(s[i][j]=='H')
                    {
                        house.push_back(node(i,j));
                    }
                }
            int st=0,ed=pp<<1|1;
            rep1(i,pp)
            {
                build(st,i,1,0);
            }
            rep1(i,pp)
            {
                build(pp+i,ed,1,0);
            }
            rep0(i,pp)
                rep0(j,pp)
                {
                    build(i+1,pp+j+1,1,abs(man[i].x-house[j].x)+abs(man[i].y-house[j].y));
                }
            min_cost_max_flow(st,ed);
            pt(c);
        }
        return 0;
    }
    View Code

     第四部分  网络流算法的应用

    一.  最大流问题。

    一般情况下,比较裸的最大流几乎不存在,网络流这种东西考得就是你的构图能力,要不然大家背一背基本算法就都满分了,下面介绍一道比较典型的最大流问题。

       问题一:最小路径覆盖问题。

       题目链接:http://hzoi.openjudge.cn/never/1004/

       最小路径覆盖=|P|-最大匹配数

       而最大匹配数可以用匈牙利,也可以用最大流,而两者在这特殊的图中,效率是相同的,而一旦题目有一些变化,网络流可以改改继续用,而匈牙利的局限性较大。

       问题二:奶牛航班。

       Usaco的赛题,以飞机上的座位作为流量限制,通过实际模型的构建,最终运用最大流算法解决,详解可参考国家集训队论文,具体哪年的忘记了,囧。

      最大流实在难已以找到比较有意思的题目,下面进入应用最广泛的最小费用流吧!

     

    .最小费用流问题(最大收益流问题)

    这个问题的模型很多下面就此解析几道例题。

       问题一:N方格取数

       在一个有m*n 个方格的棋盘中,每个方格中有一个正整数。现要从方格中取数,使任意2 个数所在方格没有公共边,且取出的数的总和最大。

       解析:这是一个二分图最大点权独立集问题,就是找出图中一些点,使得这些点之间没有边相连,这些点的权值之和最大。独立集与覆盖集是互补的,求最大点权独立集可以转化为求最小点权覆盖集(最小点权支配集)。最小点权覆盖集问题可以转化为最小割问题解决。

       结论:最大点权独立集 = 所有点权 - 最小点权覆盖集 = 所有点权 - 最小割集 = 所有点权 - 网络最大流。

       问题还有许多,可以参考网上的网络流与线性规划24题,里面题目比较全面(虽然好多根本用不到网络流)。

    最后再提一道题目,说一下最小割的转化建模。

    The last问题:黑手党

    题目大意:要用最少的人数来切断从A到B的所有路径,每个人只能切断一条边。

    分析:显然是一个从A到B的最小割问题,由最大流最小割定理,求A到B 的最大流即可。

    结论:网络流问题博大精深,难点在构图,这是一种能力,需要逐渐培养。

     

    总结:关于网络流的介绍到这里也就结束了,但是网络流绝不是仅仅这点东西的,由于个人水平问题,出错或片面的地方还请大牛指正。

    网络流题目汇总:

    poj 3281 dining 最大流+拆点(把一头牛拆成两头牛,建立边,边权为1)

    /**************************************************************
        Problem:
        User: youmi
        Language: C++
        Result: Accepted
        Time:
        Memory:
    ****************************************************************/
    //#pragma comment(linker, "/STACK:1024000000,1024000000")
    //#include<bits/stdc++.h>
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <map>
    #include <stack>
    #include <set>
    #include <sstream>
    #include <cmath>
    #include <queue>
    #include <string>
    #include <vector>
    #define zeros(a) memset(a,0,sizeof(a))
    #define ones(a) memset(a,-1,sizeof(a))
    #define sc(a) scanf("%d",&a)
    #define sc2(a,b) scanf("%d%d",&a,&b)
    #define sc3(a,b,c) scanf("%d%d%d",&a,&b,&c)
    #define scs(a) scanf("%s",a)
    #define sclld(a) scanf("%I64d",&a)
    #define pt(a) printf("%d
    ",a)
    #define ptlld(a) printf("%I64d
    ",a)
    #define rep0(i,n) for(int i=0;i<n;i++)
    #define rep1(i,n) for(int i=1;i<=n;i++)
    #define rep_1(i,n) for(int i=n;i>=1;i--)
    #define rep_0(i,n) for(int i=n-1;i>=0;i--)
    #define Max(a,b) (a)>(b)?(a):(b)
    #define Min(a,b) (a)<(b)?(a):(b)
    #define lson (step<<1)
    #define rson (lson+1)
    #define esp 1e-6
    #define oo 0x3fffffff
    
    #define TEST cout<<"*************************"<<endl
    
    using namespace std;
    typedef long long ll;
    
    int fd,dk,cw;
    
    const int maxn=600+10;
    struct node
    {
        int v,res,w;
    };
    vector<node>vt[maxn];
    void build(int u,int v,int w)
    {
        vt[u].push_back((node){v,vt[v].size(),w});
        vt[v].push_back((node){u,vt[u].size()-1,0});
        //printf("vt[%d].rev->%d   vt[%d].rev->%d
    ",u,vt[v].size()-1,v,vt[u].size()-1);
    }
    bool vis[maxn];
    bool flag[maxn];
    int dfs(int u,int t,int flow)
    {
        if(u==t)
            return flow;
        vis[u]=true;
        for(int i=0;i<vt[u].size();i++)
        {
            node&temp=vt[u][i];
            int v=temp.v;
            if(!vis[v]&&temp.w>0)
            {
                int f=dfs(v,t,Min(temp.w,flow));
                if(f)
                {   //printf("u->%d v->%d f->%d
    ",u,v,f);
                    temp.w-=f;
                    vt[v][temp.res].w+=f;
                    return f;
                }
            }
        }
        return 0;
    }
    int max_flow()
    {
        int ans=0,flow;
        int temp=fd+dk+cw+cw+1;
        while(1)
        {
            zeros(vis);
            flow=dfs(0,temp,1);
            if(flow)
                ans+=flow;
            else
                return ans;
        }
    }
    
    int main()
    {
        //freopen("in.txt","r",stdin);
        while(~sc3(cw,fd,dk))
        {
            zeros(vt);
            rep1(i,cw)
            {
                int tot1,tot2,t1,t2;
                sc2(tot1,tot2);
                rep1(j,tot1)
                {
                    sc(t1);
                    build(dk+cw+i,dk+cw+cw+t1,1);
                }
                rep1(j,tot2)
                {
                    sc(t2);
                    build(t2,dk+i,1);
                }
            }
            int temp=fd+dk+cw+cw+1;
            rep1(i,dk)
                build(0,i,1);
            rep1(i,fd)
                build(dk+cw+cw+i,temp,1);
            rep1(i,cw)
                build(dk+i,dk+cw+i,1);
            pt(max_flow());
        }
        return 0;
    }
    View Code

    (更新未完)

    不为失败找借口,只为成功找方法
  • 相关阅读:
    Java正则表达式, 提取双引号中间的部分
    如何快速找到未知长度单链表的中心点的值
    西格玛
    对数
    jquery显示隐藏toggle
    JavaScript:改变li前缀图片和样式
    jquery点击改变图片src源码并toggle
    jquery点击改变class并toggle
    linux下合并两个文件夹
    编译安装httpd
  • 原文地址:https://www.cnblogs.com/youmi/p/4712960.html
Copyright © 2011-2022 走看看