最大流比较经典的题目,运用了一定的技巧
题意:
农场主有几头牛,每头牛都有想吃的和不想吃的,某天农场主忘记按照牛的喜好做饭,但是希望能尽可能的让更多的牛满足
每头牛都有可以喝的,和可以吃的,求可以的满足的牛的数量
第一次猛一看,二分匹配!!但是仔细想想,二分匹配好像解决不了这个问题。
因为二分匹配都是两两匹配,但是这道题中,每头牛要匹配喝的喝吃的,三者之间的匹配,如果强行二分匹配,两两匹配的话
无法保证尽量的让牛满足。例如:牛1吃food1 没有匹配到喝的。牛2没有吃的但是喝 drink1 。这样两头牛都没有满足。但是如果让牛1不吃,让牛2吃,那么牛2就满足了,这种情况是二分匹配满足不了的
这道题重点就是如何变成最大流问题
变成最大流主要有几个问题
1.如何建图?
2.如果简单的建图,那么会出现一头牛吃多种食物的情况,如何解决?
主要思路:
1.建图很好建图
自己创一个源点和汇点,然后让源点指向食物,食物指向牛,牛指向饮料,每条边的流量为1
这样一会在广搜的时候的邻接矩阵就建好了(当然用邻接表更OJBK,这里为了更简单一点,用邻接矩阵)
这样图就建好了
2.上面说的方法建图有很大的问题!!!
例如:F1->N1->D1 这是一条线,一种匹配方法
那么第二次匹配的时候 F2->N1->D2 这又是一种匹配方法
第一次匹配的时候,F1->N1的流量用完了,但是F2->N1的流量还有,第二次匹配也可以成功
这就是传说中的一牛吃两草(多种食物都给同一头牛吃了)
这导致只有一头牛,但是输出答案是2。
这时,就有新操作了!!
让牛指向牛!!(这种操作我也是从别人那里学的)牛一分为二,左牛和右牛
匹配的路线变为F1->N1->N1->D1
第二次匹配的路线变为F2->N1->N1->D2
但是!!第二次匹配的路线中 N1->N1这条路线流量用完了,所以第二次匹配的路线不可能匹配成功
这样就防止了多种食物匹配一头牛
#include<queue>
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
const int maxn=100+10;
const int inf=0x3f3f3f3f;
int e[maxn*5][maxn*5];
int n,f,d;
int EK(int s,int t)
{
int f=0;
queue<int>q;
int p[maxn*5],vis[maxn*5],a[maxn*5];
while(1)
{
memset(a,0,sizeof(a));
a[s]=1;
q.push(s);
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i=0; i<=t; i++)
{
if(!a[i]&&e[u][i])
{
p[i]=u;
q.push(i);
a[i]=min(a[u],e[u][i]);
if(i==t)break;
}
}
}
if(a[t]==0)
break;
for(int i=t; i!=s; i=p[i])
{
e[p[i]][i]-=a[t];
e[i][p[i]]+=a[t];
}
f+=a[t];
}
return f;
}
int main()
{
while(~scanf("%d%d%d",&n,&f,&d))
{
memset(e,0,sizeof(e));
int a,b,num=0,t=n;
while(t--)
{
num++;
scanf("%d%d",&a,&b);
int x,y;
for(int i=1; i<=a; i++)
{
scanf("%d",&x);
e[x][num+f]=1;//食物指向牛
}
for(int j=1; j<=b; j++)
{
scanf("%d",&y);
e[num+f+n][y+2*n+f]=1;//牛指向饮料
}
}
for(int i=1; i<=n; i++)//牛指向牛
{
e[i+f][i+n+f]=1;
}
for(int i=1; i<=f; i++) //源点指向食物
{
e[0][i]=1;
}
for(int i=1; i<=d; i++) //饮料指向汇点
{
e[i+2*n+f][2*n+f+d+1]=1;
}
// for(int i=0; i<=n; i++)
// {
// for(int j=0; j<=2*n+d+f+1; j++)
// {
// printf("%d ",e[i][j]);
// }
// printf("\n");
// }
printf("%d\n",EK(0,2*n+f+d+1));
}
}