这又是一道涉及到拆点的问题,有了上一题的经验,这一题思路就打开了
我们这样拆点
因为一头牛只能喝一种饮料,吃一种食物,而且一种食物,一种饮料 吃过了 就没了
如果单纯的构建
超级源点 --- 饮料 ----- 牛 ------ 食物 ------ 超级汇点
把源点到饮料 食物到汇点边容量设为1,能保证不会大家分食物,
这种图,很显然 会出现一头牛吃多个东西 ,就出错了。
那么 我们不如构造一个这样的图 :
超级源点 --- 饮料 ----- 牛入点 ------ 牛出点 ------ 食物 ------ 超级汇点
这样拆了点之后,我们就达成了 限流这一思路 :
这也是网络流里一个思想,无论你过来的有多大,我只要把某个关键边的容量限制住,整个大流量就会被控制住,那么我们把点拆分完之后,强制要求 牛入 ------ 牛出 之间容量只为 1 也就是说 你只能吃一个,不能够多吃,这道问题也就顺利解决了,不得不说网络流的灵活利用还是太重要了,尤其是拆点。
以下是 AC 代码
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int maxn = 1e6 + 5;
const int INF = 0x3f3f3f3f;
struct node
{
int v,w;
int nxt;
}ed[maxn];
int num;
int head[maxn],h[maxn];
void adde(int u,int v,int w)
{
ed[++num].v = v;
ed[num].nxt = head[u];
ed[num].w = w;
head[u] = num;
ed[++num].v = u;
ed[num].nxt = head[v];
ed[num].w = 0;
head[v] = num;
}
int st,en;
bool bfs()
{
memset(h,0,sizeof(h));
h[st]=1;
queue<int> q;
q.push(st);
while(q.size())
{
int u = q.front();
q.pop();
for(int i=head[u];i;i=ed[i].nxt)
{
int v = ed[i].v;
if(ed[i].w && h[v] == 0)
{
h[v] = h[u] + 1;
q.push(v);
}
}
}
if(h[en] == 0)
return false;
else
return true;
}
int dfs(int u,int val)
{
int rec;
if(u == en)
return val;
for(int i=head[u];i;i=ed[i].nxt)
{
int v = ed[i].v;
if(h[v] == h[u] + 1 && ed[i].w && (rec = dfs(v, min(val, ed[i].w))))
{
ed[i].w -= rec;
ed[i ^ 1].w += rec;
return rec;
}
}
return 0;
}
int dinic()
{
int ans = 0;
int minf;
while(bfs())
while((minf = dfs(1, INF)))
ans += minf;
return ans;
}
int n,f,d;
int main()
{
scanf("%d%d%d",&n,&f,&d);
num=1;
st=1;
en=1+f+n+d+1;
for(int i=1;i<=f;i++)
{
adde(st,1+i,1);
}
for(int i=1;i<=d;i++)
{
adde(1+f+n+i,en,1);
}
for(int i=1;i<=n;i++)
{
adde(1+f+i,1+f+n+d+1+i,1);
}
for(int i=1;i<=n;i++)
{
int dn,fn;
scanf("%d%d",&fn,&dn);
for(int q=1;q<=fn;q++)
{
int fi;
scanf("%d",&fi);
adde(1+fi,1+f+i,1);
}
for(int q=1;q<=dn;q++)
{
int di;
scanf("%d",&di);
adde(1+f+n+d+1+i,1+f+n+di,1);
}
}
printf("%d\n",dinic());
return 0;
}