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

    (更新未完)

    不为失败找借口,只为成功找方法
  • 相关阅读:
    Good Bye 2014 B. New Year Permutation(floyd )
    hdu 5147 Sequence II (树状数组 求逆序数)
    POJ 1696 Space Ant (极角排序)
    POJ 2398 Toy Storage (叉积判断点和线段的关系)
    hdu 2897 邂逅明下 (简单巴什博弈)
    poj 1410 Intersection (判断线段与矩形相交 判线段相交)
    HDU 3400 Line belt (三分嵌套)
    Codeforces Round #279 (Div. 2) C. Hacking Cypher (大数取余)
    Codeforces Round #179 (Div. 2) B. Yaroslav and Two Strings (容斥原理)
    hdu 1576 A/B (求逆元)
  • 原文地址:https://www.cnblogs.com/youmi/p/4712960.html
Copyright © 2011-2022 走看看