POJ3281:Dining(dinic+拆点)
题目链接:http://poj.org/problem?id=3281
PS:刷够网络流了,先这样吧,之后再刷,慢慢补。
题意:有F种食物,D种饮料,N头奶牛,只能吃某种食物和饮料(而且只能吃特定的一份),一种食物被一头牛吃了之后,其余牛就不能吃了 第一行有N,F,D三个整数:接着2-N+1行代表第i头牛,前面两个整数是Fi与Di(食物与饮料的种类数量),接着是食物的种类与饮料的种类 要求输出最多分配能够满足的牛的数量.
思路:这是一种神奇的建图方式-拆点。让我想打死都想不出来。sad
建图,有2*n+f+d+2个顶点,0表示源点,2*n+f+d+1表示汇点。 由源点指向食物,再由食物指向牛,牛再指向对应的饮料,饮料再指向汇点 当然要使每一头牛都对应每一份食物与饮料,所以应该牛i指向牛i再指向饮料,这样就可以避免一头牛只占用多份食物与饮料了 全部是有向的边,而且权值全部为1 我在这里是1到f为食物点,f+1到f+2*n为牛点,f+2*n+1到f+2*n+d为饮料点
#include <iostream> #include <stdio.h> #include <string.h> #include <algorithm> #include <queue> #include <math.h> #define N 1100 #define inf 0x3f3f3f3f typedef int ll; using namespace std; ll n,f,d,tt,dis[N],head[N]; struct node { ll x,y,w,next; } eg[N*N]; void init() { tt=0; memset(head,-1,sizeof(head)); } void add(int xx,int yy) { eg[tt].x=xx; eg[tt].y=yy; eg[tt].w=1; eg[tt].next=head[xx]; head[xx]=tt++; eg[tt].x=yy; eg[tt].y=xx; eg[tt].w=0; eg[tt].next=head[yy]; head[yy]=tt++; } bool bfs(int s,int e) { memset(dis,-1,sizeof(dis)); dis[s]=0; queue<int>q; q.push(s); while(!q.empty()) { int fa=q.front(); q.pop(); for(int i=head[fa]; i!=-1; i=eg[i].next) { int v=eg[i].y; if(dis[v]==-1&&eg[i].w) { dis[v]=dis[fa]+1; q.push(v); } } } if(dis[e]>0) return true; return false; } int dinic(int s,int maxt) { if(s==2*n+f+d+1) return maxt; int a,sum=maxt; for(int i=head[s]; i!=-1; i=eg[i].next) { int v=eg[i].y; if(dis[v]==dis[s]+1&&eg[i].w>0) { a=dinic(v,min(sum,eg[i].w)); eg[i].w-=a; eg[i+1].w+=a; sum-=a; } } return maxt-sum; } int main() { ll xx,yy,s1,s2; scanf("%d%d%d",&n,&f,&d); init(); for(int j=1;j<=f;j++) { add(0,j); } for(int j=1;j<=d;j++) { add(f+2*n+j,f+2*n+d+1); } for(int i=1;i<=n;i++) { scanf("%d%d",&s1,&s2); for(int j=1;j<=s1;j++) { scanf("%d",&xx); add(xx,f+i); } for(int j=1;j<=s2;j++) { scanf("%d",&yy); add(f+n+i,f+2*n+yy); } } for(int i=1;i<=n;i++) { add(f+i,f+n+i); } ll ans=0; while(bfs(0,2*n+f+d+1)) { ans+=dinic(0,inf); } printf("%d\n",ans); return 0; }