洛谷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&&in;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;
}
复制代码

 

posted @   liyishui  阅读(48)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
点击右上角即可分享
微信分享提示