题目大意:
有一个酒店有p种菜,q种房间,n个客人,每个客人都有着自己喜欢的菜和房间(可能会有多种),使一个客人满意当且仅当他的吃上自己喜欢的菜何住上自己喜欢的房间,问酒店老板最大可以使几个人满意。
思路:
好像只有菜或者房间的情况就是最大二分图匹配,但是这里有三种点,每种点都只可以选一次,且三个点都要对应才可以算得上是一种匹配。
考虑最大流建模,1的流量要流过三个对应的点才代表是一种匹配,我们可以依次从菜->人->房间连边,菜和房间单独向源点和汇点连边,但是这样发现会有问题,即我们限制不了中间的人的点的流量。一个好的办法就是将一个人拆成一条流量为1边的两端的两个点,流经一个人就相当于流经这一条边,这样就通过将点化为边的形式满足了题目的限制。
/*==========================
* Ahthor : ylsoi
* Problem : luogu 1402
* Algorithm : Dinic
* Time : 2018.6.18
* ========================*/
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<climits>
#include<queue>
using namespace std;
void File(){
freopen("luogu1402.in","r",stdin);
freopen("luogu1402.out","w",stdout);
}
template<typename T>bool chkmax(T &_,T __){return _<__ ? (_=__,1) : 0;}
template<typename T>bool chkmin(T &_,T __){return _>__ ? (_=__,1) : 0;}
#define REP(i,a,b) for(register int i=a;i<=b;++i)
#define DREP(i,a,b) for(register int i=a;i>=b;--i)
#define MREP(i,x) for(register int i=beg[x];i;i=E[i].last)
#define mem(a) memset(a,0,sizeof(a))
#define ll long long
#define inf INT_MAX
const int maxn=100+10;
int n,p,q,beg[maxn*5],cnte=1,ss,tt;
struct edge{int to,last,flow;}E[maxn*maxn*5];
void add(int u,int v,int flow){
++cnte;
E[cnte].to=v;
E[cnte].last=beg[u];
beg[u]=cnte;
E[cnte].flow=flow;
}
void init(){
scanf("%d%d%d",&n,&p,&q);
ss=0;tt=n*2+p+q+1;
REP(i,1,n)REP(j,1,p){
int tmp;
scanf("%d",&tmp);
if(!tmp)continue;
add(j,p+i,1);
add(p+i,j,0);
}
REP(i,1,n)REP(j,1,q){
int tmp;
scanf("%d",&tmp);
if(!tmp)continue;
add(p+n+i,p+n+n+j,1);
add(p+n+n+j,p+n+i,0);
}
REP(i,1,n){
add(p+i,p+n+i,1);
add(p+n+i,p+i,0);
}
REP(i,1,p){
add(ss,i,1);
add(i,ss,0);
}
REP(i,1,q){
add(n*2+p+i,tt,1);
add(tt,n*2+p+i,0);
}
}
namespace dinic{
int num[maxn*5],cur[maxn*5],ans;
bool bfs(){
mem(num);
queue<int>qu;
qu.push(ss);
num[ss]=1;
while(qu.size()){
int u=qu.front();
qu.pop();
if(u==tt)return true;
MREP(i,u){
int v=E[i].to;
if(num[v] || !E[i].flow)continue;
num[v]=num[u]+1;
qu.push(v);
}
}
return false;
}
int dfs(int u,int gap){
if(u==tt || !gap)return gap;
int sum=0,f;
for(int &i=cur[u];i;i=E[i].last){
int v=E[i].to;
if(num[v]!=num[u]+1)continue;
if((f=dfs(v,min(gap,E[i].flow)))){
sum+=f;
gap-=f;
E[i].flow-=f;
E[i^1].flow+=f;
}
if(!gap)break;
}
return sum;
}
void work(){
while(bfs()){
REP(i,0,tt)cur[i]=beg[i];
ans+=dfs(ss,inf);
}
printf("%d\n",ans);
}
}
int main(){
File();
init();
dinic::work();
return 0;
}