POJ3281 Dining [最大流应用]

时间:2021-10-18 04:31:25

2012/2/25 更新。

题意:

每只牛有他喜欢的食物和饮料,问怎样匹配可以使能吃到满意的食物和饮料的牛的数量最大。

思路:

匹配问题。最大流的应用。难点在构图。

我对最大流的理解就是:应用在匹配,关键就是利用边的占用,每一条增广路对边都有一定的占用,每个匹配就类似一次占用。

所以只要在构图的时候牛在中间,食物在左边,饮料在右边,源点s连着食物,汇点t连着饮料,每条边权值都为1。

这样就可以保证每个食物和牛的唯一匹配,且每个饮料对牛的唯一匹配。

但是此时有个问题,就是对牛没有约束,实际上每只牛对食物或者饮料都是唯一的,就是一只牛不能匹配多个食物。

所以将牛进行拆点,拆成两个点,此两点中间有一条权值为1的边。这样就保证匹配唯一性。


#include<iostream>
#include<vector>
#include<string>
#include<queue>
#include<algorithm>
#define llong long long
#define Min(a,b) (a<b?a:b)
#define Max(a,b) (a>b?a:b)
#define Abs(a) ((a)>0?(a):-(a))
#define Mod(a,b) (((a)-1+(b))%(b)+1)
using namespace std;
const int N=405;
const int M=60005;
const int inf=1e10;
int n,fd,dk;
struct 
{
	int v,re,next,w;
}edge[M];
int edgehead[N];
bool vis[N];
int level[N];
int k;
void addedge(int u ,int v,int w)
{
	edge[k].v=v;
	edge[k].w=w;
	edge[k].re=k+1;
	edge[k].next=edgehead[u];
	edgehead[u]=k++;

	edge[k].v=u;
	edge[k].re=k-1;
	edge[k].w=0;
	edge[k].next=edgehead[v];
	edgehead[v]=k++;
}
bool bfs(int s,int t)
{
	memset(vis,0,sizeof(vis));
	memset(level,0,sizeof(level));
	vis[s]=true;
	level[s]=0;
	queue<int> que;
	que.push(s);
	while(!que.empty())
	{
		int now=que.front();
		que.pop();
		if(now==t)
			return true;
		for(int i=edgehead[now];i;i=edge[i].next)
		{
			int v=edge[i].v;
			int w=edge[i].w;
			if(!vis[v]&&w)
			{
				level[v]=level[now]+1;
				vis[v]=true;
				que.push(v);
			}
		}
	}
	return false;
}
int dinic(int now,int sum,int t)
{
	if(now==t)
		return sum;
	int os=sum;
	for(int i=edgehead[now];i && sum>0;i=edge[i].next)
	{
		int v=edge[i].v;
		int w=edge[i].w;
		if(level[v]==level[now]+1 && w)
		{
			int tmp=dinic(v,Min(w,sum),t);
			edge[i].w-=tmp;
			edge[edge[i].re].w+=tmp;
			sum-=tmp;
		}
	}
	return os-sum;
}
void solve(int s,int t)
{
	int ans=0;
	while(bfs(s,t))
		ans+=dinic(s,inf,t);
	printf("%d\n",ans);
}
int main()
{
	scanf("%d%d%d",&n,&fd,&dk);
	int tmp1,tmp2;
	k=1;
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",&tmp1,&tmp2);
		int v;
		for(int j=1;j<=tmp1;j++)
		{
			scanf("%d",&v);
			addedge(v+n*2,i,1);
		}
		for(int j=1;j<=tmp2;j++)
		{
			scanf("%d",&v);
			addedge(i+n,v+n*2+fd,1);
		}
	}
	for(int i=1;i<=n;i++)
		addedge(i,n+i,1);
	int s=2*n+fd+dk+1;
	int t=s+1;
	for(int i=1;i<=fd;i++)
		addedge(s,2*n+i,1);
	for(int i=1;i<=dk;i++)
		addedge(2*n+fd+i,t,1);
	solve(s,t);
	return 0;
}