洛谷P1402 酒店之王
前置知识:最大流
大意是一个人喜欢一些菜,还喜欢一些房间,但房间和菜只能对应一个人,求最大的匹配数
菜和房间都是围绕人开始的,所以建图时把人放中间
room-->people-->veg or
veg-->people-->room
为什么不能是people-->room-->veg?
你会发现这样room和veg之间没有什么联系,建不了图
注意到房间和菜都只能使用一次,因此需要拆点
如果不拆点的话,可能会出现非法情况:
(其中r表示room,p表示people,v表示veg,边上的数字1表示容量1)
拆点是网络流里很常见的经典技巧,经常搭配使用限制次数(这个思想可以去看看P2472[SCOI]2007蜥蜴)
如果不是很明白为什么拆点后就能限制只用一次的话,可以自己动手画小样例模拟一下
关于建图的具体操作:
/* 房间 人 菜 1-p 2p+1-2p+n 2p+2n+1-2p+2n+q p-2p 2p+n-2p+2n 2p+2n+q-2p+2n+2q */
超级源点S向房间的入口建边,容量1
菜的出口向超级汇点T建边,容量1
点和点之间建边,容量1(同理,如果不止一次,容量随之更改)
人喜欢一个房间,房间的出口与人的入口连,容量1
人喜欢菜,人的出口和菜的入口连
人,菜,房间的出入口自己连
s=0,t=2*(n+p+q)+1; for(int i=1;i<=n;i++) { for(int j=1;j<=p;j++) { int x; cin>>x; if(x==1) {//听说你喜欢房间 add(j+p,2*p+i,1);add(i+2*p,j+p,0); // j+p 是房间出口 2*p+i是客人入口 } } } for(int i=1;i<=n;i++) { for(int j=1;j<=q;j++) { int x; cin>>x; if(x==1) {//听说你还喜欢菜 add(i+2*p+n,2*n+2*p+j,1);add(2*n+2*p+j,i+2*p+n,0); //人的出口&&菜的入口 } } } for(int i=1;i<=p;i++) { add(i,i+p,1);add(i+p,i,0); } for(int i=1;i<=n;i++) { add(2*p+i,2*p+n+i,1);add(2*p+n+i,2*p+i,0); } for(int i=1;i<=q;i++) { add(2*p+2*n+i,2*p+2*n+q+i,1);add(2*p+2*n+q+i,2*p+2*n+i,0); } for(int i=1;i<=p;i++) { add(0,i,1);add(i,0,0); } for(int i=1;i<=q;i++) { add(2*p+2*n+q+i,t,1);add(t,2*p+2*n+q+i,0); }
剩下的就是跑最大流的板子了,图论的难点一般在建模,想清楚思路码起来会舒服很多
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=50000; struct lys{ ll from,to,nxt,c; }e[maxn*4]; ll cnt=1,s,t,dep[maxn],head[maxn],val[maxn]; void add(int from,int to,ll c) { cnt++; e[cnt].from=from;e[cnt].to=to;e[cnt].nxt=head[from];head[from]=cnt;val[cnt]=c; } bool bfs() { queue<int>q; memset(dep,0,sizeof(dep)); q.push(s); dep[s]=1; while(!q.empty()) { int u=q.front(); q.pop(); for(int i=head[u];i;i=e[i].nxt) { int to=e[i].to; if(val[i]&&!dep[to]) { dep[to]=dep[u]+1; q.push(to); } } } return dep[t]; } ll dfs(int u,ll in) { if(u==t) return in; ll out=0; for(int i=head[u];i&∈i=e[i].nxt) { int to=e[i].to; if(val[i]&&dep[to]==dep[u]+1) { ll res=dfs(to,min(val[i],in)); val[i]-=res; val[i^1]+=res; in-=res; out+=res; } } if(!out) dep[u]=0; return out; } int n,p,q; int main() { //freopen("lys.in","r",stdin); cin>>n>>p>>q; //人数 房间数 菜数 /* 房间 人 菜 1-p 2p+1-2p+n 2p+2n+1-2p+2n+q p-2p 2p+n-2p+2n 2p+2n+q-2p+2n+2q */ s=0,t=2*(n+p+q)+1; for(int i=1;i<=n;i++) { for(int j=1;j<=p;j++) { int x; cin>>x; if(x==1) {//听说你喜欢房间 add(j+p,2*p+i,1);add(i+2*p,j+p,0); // j+p 是房间出口 2*p+i是客人入口 } } } for(int i=1;i<=n;i++) { for(int j=1;j<=q;j++) { int x; cin>>x; if(x==1) {//听说你还喜欢菜 add(i+2*p+n,2*n+2*p+j,1);add(2*n+2*p+j,i+2*p+n,0); //人的出口&&菜的入口 } } } for(int i=1;i<=p;i++) { add(i,i+p,1);add(i+p,i,0); } for(int i=1;i<=n;i++) { add(2*p+i,2*p+n+i,1);add(2*p+n+i,2*p+i,0); } for(int i=1;i<=q;i++) { add(2*p+2*n+i,2*p+2*n+q+i,1);add(2*p+2*n+q+i,2*p+2*n+i,0); } for(int i=1;i<=p;i++) { add(0,i,1);add(i,0,0); } for(int i=1;i<=q;i++) { add(2*p+2*n+q+i,t,1);add(t,2*p+2*n+q+i,0); } ll ans=0; while(bfs()) ans+=dfs(s,1e18); cout<<ans; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)