POJ 3281 Dining(拆点+最大流)

来源:互联网 发布:用编程代码说我爱你 编辑:程序博客网 时间:2024/05/22 00:11

这里是传送门

Dining
Time Limit: 2000MS Memory Limit: 65536KTotal Submissions: 19123 Accepted: 8534

Description

Cows are such finicky eaters. Each cow has a preference for certain foods and drinks, and she will consume no others.

Farmer John has cooked fabulous meals for his cows, but he forgot to check his menu against their preferences. Although he might not be able to stuff everybody, he wants to give a complete meal of both food and drink to as many cows as possible.

Farmer John has cooked F (1 ≤ F ≤ 100) types of foods and prepared D (1 ≤ D ≤ 100) types of drinks. Each of his N (1 ≤ N ≤ 100) cows has decided whether she is willing to eat a particular food or drink a particular drink. Farmer John must assign a food type and a drink type to each cow to maximize the number of cows who get both.

Each dish or drink can only be consumed by one cow (i.e., once food type 2 is assigned to a cow, no other cow can be assigned food type 2).

Input

Line 1: Three space-separated integers: NF, and D 
Lines 2..N+1: Each line i starts with a two integers Fi and Di, the number of dishes that cow i likes and the number of drinks that cow i likes. The next Fi integers denote the dishes that cow i will eat, and theDi integers following that denote the drinks that cow i will drink.

Output

Line 1: A single integer that is the maximum number of cows that can be fed both food and drink that conform to their wishes

Sample Input

4 3 32 2 1 2 3 12 2 2 3 1 22 2 1 3 1 22 1 1 3 3

Sample Output

3

Hint

One way to satisfy three cows is: 
Cow 1: no meal 
Cow 2: Food #2, Drink #2 
Cow 3: Food #1, Drink #1 
Cow 4: Food #3, Drink #3 
The pigeon-hole principle tells us we can do no better since there are only three kinds of food or drink. Other test data sets are more challenging, of course.

题意是有n头牛,每头牛有自己喜欢的食物和饮料,共有f种食物,d种饮料,问最大匹配是多少。


拆点最大流问题,把每头牛拆点,拆点后考虑食物跟饮料怎么连接,若是牛在两侧,这样饮料跟食物会有重合,构图无法跑最大流。所以只能牛放中间,饮料跟食物放在两侧。建立超级源点跟超级汇点,所有连接的流量都为1,食物连接源点,饮料连接汇点。最后跑一遍最大流,得到结果。


用Edmonds-Karp跟链式前向星都可以做,前者耗时391ms,耗内存2.7MB,后者耗时16ms,耗内存0.8MB,若数据再大些,只能用后者,听说所有的最大流都不会卡链式前向星建图。


代码直接套模板,可能会有些莫名其妙的注释。


#include<iostream>#include<algorithm>#include<cstring>#include<cmath>#include<queue>#include<cstdio>#define ll long long#define mset(a,x) memset(a,x,sizeof(a))using namespace std;const double PI=acos(-1);const int inf=0x3f3f3f3f;const double eps=1e-6;const int maxn=500;const int mod=1e9+7;int dir[4][2]={0,1,1,0,0,-1,-1,0};ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}ll lcm(ll a,ll b){return a/gcd(a,b)*b;}ll inv(ll b){if(b==1)return 1; return (mod-mod/b)*inv(mod%b)%mod;}int map[maxn][maxn],flow[maxn][maxn],a[maxn],path[maxn];int n,m,start,END;int maxflow(){int i,j,k;queue <int> q;mset(flow,0);int max_flow=0;while(1){mset(a,0);a[start]=inf;while(!q.empty()) q.pop();q.push(start);while(!q.empty()){int temp=q.front();q.pop();//if(temp==END)//break;for(i=0;i<=END;i++){if(!a[i]&&flow[temp][i]<map[temp][i]){path[i]=temp;a[i]=min(a[temp],map[temp][i]-flow[temp][i]);q.push(i);}}}if(a[END]==0)break;for(j=END;j!=start;j=path[j]){flow[path[j]][j]+=a[END];       //反向弧相加             flow[j][path[j]]-=a[END];       //减去流量 }max_flow+=a[END];}return max_flow;}int main(){int x,y,z,temp,i,j,k,a,b,c;while(~scanf("%d%d%d",&n,&y,&z)){mset(map,0);start=0;END=2*n+y+z+1;               //超级源点0,超级汇点所有点的和加1 for(i=1;i<=y;i++)                    //每种食物连接超级源点,流量为1 map[0][i]=1;for(i=1;i<=n;i++)                    //拆点 map[y+i][y+n+i]=1;for(i=1;i<=z;i++)                    //饮料跟超级汇点连接,流量为1 map[y+2*n+i][y+2*n+z+1]=1;for(i=1;i<=n;i++){scanf("%d%d",&a,&b);for(j=0;j<a;j++){scanf("%d",&c);              //食物与牛连接,流量为1 map[c][i+y]=1;}for(k=0;k<b;k++){scanf("%d",&c);             //饮料与拆点后的牛连接,流量为1 map[y+n+i][y+2*n+c]=1;}}cout<<maxflow()<<endl;}return 0;}


#include<iostream>#include<algorithm>#include<cstring>#include<cmath>#include<queue>#include<cstdio>#define ll long long#define mset(a,x) memset(a,x,sizeof(a))using namespace std;const double PI=acos(-1);const int inf=0x3f3f3f3f;const double esp=1e-6;const int maxn=405;const int mod=1e9+7;int dir[4][2]={0,1,1,0,0,-1,-1,0};struct node{int v,w,next;}edge[300005];int head[maxn],d[maxn],start,END,cnt,sum,n;void add(int u,int v,int w)            //spfa建图 {edge[cnt].v=v;    edge[cnt].w=w;    edge[cnt].next=head[u];    head[u]=cnt++;    edge[cnt].v= u;    edge[cnt].w=0;    edge[cnt].next=head[v];    head[v]=cnt++;}int bfs(){int i,j,k;mset(d,0);d[start]=1;queue <int> q;q.push(start);while(!q.empty()){int temp=q.front();q.pop();if(temp==END)                   //到达汇点 return 1;for(i=head[temp];i!=-1;i=edge[i].next) //搜增广路 {int temp1=edge[i].v;               //下一可连通点 int temp2=edge[i].w;               //当前流量 if(temp2&&d[temp1]==0)             //temp1没走到 {d[temp1]=d[temp]+1;            //点加1 if(temp1==END)                 //找到增广路 return 1;q.push(temp1);                 //入队列 }}}return 0;}int dfs(int u,int maxflow){if(u==END)return maxflow;int i,j,ret=0;for(i=head[u];i!=-1;i=edge[i].next){int temp1=edge[i].v;            //下一个可连通点 int temp2=edge[i].w;            //当前点的流量 if(temp2&&d[temp1]==d[u]+1){int temp=dfs(temp1,min(maxflow-ret,temp2)); //下一点,取最大流跟当前流量小的一个 edge[i].w-=temp;                  //正向相减 edge[i^1].w+=temp;                //反向弧相加 ret+=temp;                        //最大流 if(ret==maxflow)                  //下一点最大流等于这一点 return ret;}}return ret;}int dinic(){int ans=0;while(bfs()==1){ans+=dfs(start,inf);      //dfs返回每次能出去的蜥蜴的数量 }return ans;}int main(){int x,y,z,temp,i,j,k;while(~scanf("%d%d%d",&n,&y,&z)){mset(head,-1);cnt=0;start=0;END=2*n+y+z+1;for(i=1;i<=n;i++){scanf("%d%d",&x,&temp);while(x--){scanf("%d",&j);add(n*2+j,i,1);}while(temp--){scanf("%d",&j);add(n+i,n*2+y+j,1);}add(i,n+i,1);}for(i=1;i<=y;i++){add(0,n*2+i,1);}for(i=1;i<=z;i++){add(n*2+y+i,n*2+y+z+1,1);}cout<<dinic()<<endl;}return 0;}


原创粉丝点击