题目大意:
n 头牛 f 种食物 d 种饮料
每头牛有各自喜欢的食物和饮料
求最多有多少头牛能分配到自己喜欢的食物和饮料
因为同时有食物和饮料 所以不能用二分图匹配
用最大流解决二分图匹配的办法
增加一个源点连向所有食物 每头牛与各自喜欢的食物连边
增加一个汇点连向所有的饮料 每头牛与各自喜欢的饮料连边
以上边容量都为1
单纯这样连的话 一头牛可能分配到多种食物和饮料
把一头牛拆成两个点 一点与食物连边 另一点与饮料连边
再在两个点之间连一条容量为1的边 这样就能保证只有一个流量流过
即只有一种食物被选 饮料反过来同理
此时从源点到汇点跑个最大流就能得到答案
#include <bits/stdc++.h> #define INF 0x3f3f3f3f using namespace std; const int N=105; bool F[N][N], D[N][N]; int n, f, d; struct EDGE { int v,c,r; }; vector <EDGE> E[505]; bool vis[505]; void addE(int u,int v,int c) { E[u].push_back((EDGE){v,c,E[v].size()}); E[v].push_back((EDGE){u,0,E[u].size()-1}); } /**最大流*/ int dfs(int s,int t,int f) { if(s==t) return f; vis[s]=1; for(int i=0;i<E[s].size();i++) { EDGE& e=E[s][i]; // EDGE& 之后可直接修改到这个地址的数值 if(!vis[e.v] && e.c>0) { // 没走过且可流过 int d=dfs(e.v,t,min(f,e.c)); // 继续搜 能流过的最大流量 if(d>0) { // 说明可流过d流量 e.c-=d; // 那么这条边的剩余容量减小 E[e.v][e.r].c+=d; // 反向边剩余容量变大 return d; } } } return 0; } int maxFlow(int s,int t) { int flow=0; while(1) { memset(vis,0,sizeof(vis)); int f=dfs(s,t,INF); // 边的容量改变后 继续搜 // 边的容量改变后就可能会走反向边 // 走反向边意味着 反悔正向的流量(可能有浪费) // 即 把正向的流量调小 if(f==0) return flow; // 直到不可流 flow+=f; } } /***/ /* 0~n-1 食物一侧的牛 n~2*n-1 饮料一侧的牛 2*n~2*n+f-1 食物 2*n+f~2*n+f+d-1 饮料 */ void solve() { // 增加一个源点 s=2*n+f+d // 增加一个汇点 t=s+1 int s=2*n+f+d, t=s+1; for(int i=0;i<=t;i++) E[i].clear(); for(int i=2*n;i<2*n+f;i++) addE(s,i,1); // 源点到食物 for(int i=2*n+f;i<2*n+f+d;i++) addE(i,t,1); // 饮料到汇点 for(int i=0;i<n;i++) { addE(i,i+n,1); // 一头牛拆成的两个点连边 for(int j=0;j<f;j++) if(F[i][j]) addE(2*n+j,i,1); // 食物到牛的一点 for(int j=0;j<d;j++) if(D[i][j]) addE(n+i,2*n+f+j,1); // 另一点到饮料 } printf("%d ",maxFlow(s,t)); } int main() { while(~scanf("%d%d%d",&n,&f,&d)) { memset(F,0,sizeof(F)); memset(D,0,sizeof(D)); for(int i=0;i<n;i++) { int a,b,t; scanf("%d%d",&a,&b); while(a--) { scanf("%d",&t); F[i][t-1]=1; } while(b--) { scanf("%d",&t); D[i][t-1]=1; } } solve(); } return 0; }