题目链接
题意
n头牛,m菜,k饮料,菜和饮料一份只能给一头牛,每头牛有固定食谱只吃喝上面有的。每头牛要么不吃不喝,要么有吃有喝,问最多能有几头牛有吃有喝?
思路
牛拆点拆成in和out,in和out连边,食物向可以吃他的牛连边,牛向所有可以喝的饮料连边,源点向所有食物连边,饮料向汇点连边,所有边权都为1,跑最大流。
代码
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<string>
#include<queue>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
//#define int long long
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=10100;
const int maxe=200010;
int head[maxn],cnt;
struct Edge{
int v;
int w;
int next;
}edge[maxe];
int n,m,k,s,t;
ll maxflow;
int deep[maxn];//层级数,其实应该是level
int now[maxe];//当前弧优化
void init(){
memset(head,-1,sizeof(head));//清空所有点对应边的联系
cnt=0;
maxflow=0;
return ;
}
void add(int u,int v,int w){
edge[cnt].v=v;
edge[cnt].w=w;
edge[cnt].next=head[u];
head[u]=cnt++;
}
inline bool bfs(){//在残量网络中构造分层图
memset(deep,0x3f,sizeof(deep));
queue<int>q;
q.push(s);deep[s] = 0;now[s] = head[s];//一些初始化
while(q.size()){
int x = q.front();q.pop();
for(int i=head[x];i!=-1;i=edge[i].next){
int y=edge[i].v;
if(edge[i].w>0&&deep[y]==inf){//没走过且剩余容量大于0
q.push(y);
now[y]=head[y];//先初始化,暂时都一样
deep[y]=deep[x]+1;
if(y==t) return 1;//找到了
}
}
}
return 0;
}
//flow是整条增广路对最大流的贡献,rest是当前最小剩余容量,用rest去更新flow
ll dfs(int x,int flow){//在当前分层图上增广
if(x==t) return flow;
ll ans = 0,k,i;
for(i=now[x];i!=-1&&flow;i=edge[i].next){
now[x]=i;//当前弧优化(避免重复遍历从x出发的不可拓展的边)
int y=edge[i].v;
if(edge[i].w>0&&(deep[y]==deep[x]+1)){//必须是下一层并且剩余容量大于0
k=dfs(y,min(flow,edge[i].w));//取最小
if(!k) deep[y]=inf;//剪枝,去掉增广完毕的点
edge[i].w-=k;//回溯时更新
edge[i^1].w+=k;//成对变换
ans+=k;
flow-=k;
}
}
return ans;
}
void dinic(){
while(bfs())
maxflow+=dfs(s,inf);
}
int main(){
IOS
//1--n 牛in,n+1--2n 牛out,2n+1--2n+m 食物, 2n+m+1-2n+m+k 饮料, 2n+m+k+1源点,2n+m+k+2 汇点。
cin>>n>>m>>k;
init();
s=2*n+m+k+1,t=s+1;
for(int i=1;i<=n;i++){
int a,b,x;
cin>>a>>b;
while(a--){
cin>>x;
x+=2*n;
add(x,i,1),add(i,x,0);
}
while(b--){
cin>>x;
x+=2*n+m;
add(i+n,x,1),add(x,i+n,0);
}
}
for(int i=1;i<=n;i++)
add(i,i+n,1),add(i+n,i,0);
for(int i=1;i<=m;i++)
add(s,2*n+i,1),add(2*n+i,s,0);
for(int i=1;i<=k;i++)
add(2*n+m+i,t,1),add(t,2*n+m+i,0);
dinic();
cout<<maxflow<<endl;
return 0;
}