题解 P1402 【酒店之王】

Zesty_Fox

2020-01-30 00:22:10

Solution

[原题链接](https://www.luogu.com.cn/problem/P1402) 博客食用效果更佳:[链接](https://www.cnblogs.com/acceptedzhs/p/12241915.html) 这道题,很明显是个配对问题。于是,我们可以想到用网络最大流来做。 先整理一下题目条件。 1. 很明显,根据贪心思想,要使最多人满意,每个人应该最多睡一个房间(似乎也没有人能睡两个房间),吃一道菜。这就要求一个人最多与一个房间、一道菜配对。 2. 每个人必须住进喜欢的房间**且**吃到喜欢的菜才算满意。 3. 每道菜至多让1个人吃,每个房间至多让1个人睡。 于是,我们得出了建图的几大原则: 1. 菜与房间**分别**作为一个点。 2. 为了保证条件1,每个人应该拆成2个点,并在这两个点之间连一条容量为1的边。 3. 为了保证条件2,应该让每一个人拆成的第一个点与所有自己喜欢的房间对应的点连一条容量为1的边,让每一个人拆成的第二个点与所有自己喜欢的菜对应的点连一条容量为1的边。 4. 为了保证条件3,应该让源点$s$向所有的房间连一条容量为1的边,所有的菜向汇点$t$连一条容量为1的边。 放一张对应的图(样例): ![](https://s2.ax1x.com/2020/01/30/1QzH8H.png) 最后EK跑最大流即可。 Code(超级精简,不到60行): ```cpp #include <bits/stdc++.h> using namespace std; int n,p,q,head[10005],tot=-1,st,ed,flow[10005],pre[10005],maxflow,vis[10005]; const int INF=0x3f3f3f3f;queue<int> qq; struct Edge{int to,nxt,flow;}e[200005]; void addedge(int x,int y,int f){ e[++tot].to=y;e[tot].nxt=head[x];e[tot].flow=f;head[x]=tot; e[++tot].to=x;e[tot].nxt=head[y];e[tot].flow=0;head[y]=tot; } bool bfs(){ memset(vis,0,sizeof(vis));memset(flow,0,sizeof(flow));flow[st]=INF; while(!qq.empty()) qq.pop(); qq.push(st);vis[st]=1; while(!qq.empty()){ int x=qq.front();qq.pop(); for(int i=head[x],y;i!=-1;i=e[i].nxt){ if(e[i].flow>0){ y=e[i].to; if(vis[y]) continue; flow[y]=min(flow[x],e[i].flow);pre[y]=i; vis[y]=1;qq.push(y); if(y==ed) return 1; } } } return 0; } void doit(){ while(bfs()){ int tmp=ed;maxflow+=flow[ed]; while(tmp!=st){ e[pre[tmp]].flow-=flow[ed],e[pre[tmp]^1].flow+=flow[ed]; tmp=e[pre[tmp]^1].to; } } } int main(){ memset(head,-1,sizeof(head)); scanf("%d%d%d",&n,&p,&q); st=0,ed=2*n+p+q+1; for(int i=1;i<=n;i++){ addedge(i+p,i+n+p,1); for(int j=1;j<=p;j++){ int x;scanf("%d",&x); if(x) addedge(j,i+p,1); } } for(int i=1;i<=n;i++){ for(int j=1;j<=q;j++){ int x;scanf("%d",&x); if(x) addedge(i+p+n,n*2+p+j,1); } } for(int i=1;i<=p;i++) addedge(st,i,1); for(int i=1;i<=q;i++) addedge(i+n*2+p,ed,1); doit();printf("%d\n",maxflow); return 0; } ```