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

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

下面是摘自某牛(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\n",a)
#define ptlld(a) printf("%I64d\n",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\n",a)
#define ptlld(a) printf("%I64d\n",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\n",u,v);
            pc[u][v]=w;
        }
        rep1(i,np)
        {
            scanf(" (%d)%d",&v,&w);
            v++;
            //printf("v->%d w->%d\n",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\n",a)
#define ptlld(a) printf("%I64d\n",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\n",a)
#define ptlld(a) printf("%I64d\n",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\n",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\n",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

 

(更新未完)

posted on 2015-08-08 12:47  中子星  阅读(410)  评论(0编辑  收藏  举报