版权声明:虽然我很菜,不过转载请标明出处。 https://blog.csdn.net/Patrickpwq/article/details/86649685
将超级源点先与所有食物相连,容量为1(这代表每种食物主人只能有一个),
饮料和超级汇点相连,容量也为1(这代表饮料的主人只有一个),
随后食物与对应的牛相连,容量也为1(这代表每种食物量为1,选完就没有了),以及牛与饮料连1(这代表每种饮料量为1,选完就没有了),
不过注意到隐藏的限制条件,流入牛的流量有可能大于1,流入饮料的流量也可能大于1,这代表这头牛选了多个食物/饮料,每只牛只能选择一种食物和饮料,这样的话,我们采用拆点的方式,将牛拆为入点和出点,连1的边,就保证了流入/流出的流量最多为1
#include<bits/stdc++.h>
const int N=405;
const int M=100005;
const int INF=0x3f3f3f3f;
using namespace std;
int n,f,d,s,t;
struct Edge
{
int to,next,cap;
}edge[2*M];
int first[N],tot=1;
inline void addedge(int x,int y,int z)
{
tot++;
edge[tot].to=y; edge[tot].next=first[x]; edge[tot].cap=z; first[x]=tot;
}
int step[N],cur[N];
bool bfs()
{
memset(step,-1,sizeof(step));
queue<int> q;
q.push(s); step[s]=1;
while(!q.empty())
{
int now=q.front(); q.pop();
for(int u=first[now];u;u=edge[u].next)
{
int vis=edge[u].to;
if(step[vis]==-1&&edge[u].cap>0)
{
step[vis]=step[now]+1;
q.push(vis);
if(vis==t) return 1;
}
}
}
return 0;
}
int dfs(int now,int f)
{
if(!f||now==t) return f;
int out=0;
for(int &u=cur[now];u;u=edge[u].next)
{
int vis=edge[u].to;
if(edge[u].cap&&step[vis]==step[now]+1)
{
int w=dfs(vis,min(f,edge[u].cap));
if(w==0) continue;
out+=w; f-=w;
edge[u].cap-=w; edge[u^1].cap+=w;
if(f==0) break;
}
}
return out;
}
void Maxflow()
{
int ans=0;
while(bfs())
{
for(int i=1;i<=t;i++) cur[i]=first[i];
ans+=dfs(s,INF);
}
cout<<ans<<endl;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(NULL); cout.tie(NULL);
cin>>n>>f>>d;
s=1,t=1+f+n+n+d+1;
for(int i=2;i<=f+1;i++)
{
addedge(s,i,1);
addedge(i,s,0);
}
for(int i=1+f+n+n+1;i<=1+f+n+n+d;i++)
{
addedge(i,t,1);
addedge(t,i,0);
}
for(int i=1+f+1;i<=1+f+n;i++)
{
addedge(i,i+n,1);
addedge(i+n,i,0);
}
for(int i=1,fi,di,x;i<=n;i++)
{
cin>>fi>>di;
for(int j=1;j<=fi;j++)
{
cin>>x;
addedge(x+1,1+f+i,1);
addedge(1+f+i,x+1,0);
}
for(int j=1;j<=di;j++)
{
cin>>x;
addedge(1+f+n+i,1+f+n+n+x,1);
addedge(1+f+n+n+x,1+f+n+i,0);
}
}
Maxflow();
return 0;
}