【Luogu】P1402 酒店之王 题解

原题链接

这道题,很明显是个配对问题。于是,我们可以想到用网络最大流来做。

先整理一下题目条件。

  1. 很明显,根据贪心思想,要使最多人满意,每个人应该最多睡一个房间(似乎也没有人能睡两个房间),吃一道菜。这就要求一个人最多与一个房间、一道菜配对。
  2. 每个人必须住进喜欢的房间吃到喜欢的菜才算满意。
  3. 每道菜至多让1个人吃,每个房间至多让1个人睡。

于是,我们得出了建图的几大原则:

  1. 菜与房间分别作为一个点。
  2. 为了保证条件1,每个人应该拆成2个点,并在这两个点之间连一条容量为1的边。
  3. 为了保证条件2,应该让每一个人拆成的第一个点与所有自己喜欢的房间对应的点连一条容量为1的边,让每一个人拆成的第二个点与所有自己喜欢的菜对应的点连一条容量为1的边。
  4. 为了保证条件3,应该让源点\(s\)向所有的房间连一条容量为1的边,所有的菜向汇点\(t\)连一条容量为1的边。

放一张对应的图(样例):

最后 EK 跑最大流即可。

#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;
}

posted @ 2020-01-30 00:21  Zesty_Fox  阅读(179)  评论(0编辑  收藏  举报