P2055-假期的宿舍【网络流,最大流,最大匹配】

正题

链接:
https://www.luogu.org/record/show?rid=7930976


大意

有n个人,有的在学校有床有的没有,有的在家有的没有。现在如果有人回家了那么他就会去看望他的朋友,回家的就会空出自己的床位。每个人可以睡和自己是直接朋友关系或自己的床,要求给本来有床的并且不在家的和来看望其的朋友分配床位。


解题思路

将人和床建立二分图,我们假设每个在家的人都有床,这样不用看望朋友的就可以睡自己床,然后将除了没有床且不再家的人都作为左边点,然后将在学校的床作为右边点进行最大匹配。


代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct line{
    int to,next,w;
}a[10010];
int n,m,x,y,d[110],tot,state[110],school[110],num;
int head,tail,ls[110],s,e,ans,t,home[110],nn,qn;
bool ok[110],lxx[110][110];
void addl(int x,int y,int w)
{
    a[tot].to=y;a[tot].next=ls[x];
    a[tot].w=w;ls[x]=tot++;
    a[tot].to=x;a[tot].next=ls[y];
    a[tot].w=0;ls[y]=tot++;
}
bool bfs()
{
    head=0;tail=1;
    memset(d,-1,sizeof(d));
    d[s]=0;state[1]=s;
    do
    {
        head++;
        int x=state[head];
        for (int q=ls[x];q;q=a[q].next)
        {
            int y=a[q].to;
            if (a[q].w>0 && d[y]==-1)
            {
                d[y]=d[x]+1;
                state[++tail]=y;
                if (y==e) return true;
            }
        }
    }
    while (head<tail);
    return false;
} 
int dinic(int x,int flow)
{
    int rest=0,k;
    if (x==e) return flow;
    for (int q=ls[x];q;q=a[q].next)
    {
        int y=a[q].to;
        if (a[q].w>0 && d[y]==d[x]+1)
        {
            rest+=(k=dinic(y,min(a[q].w,flow-rest)));
            a[q].w-=k;
            a[q^1].w+=k;
            if (rest==flow) return flow;
        }
    }
    if (!rest) d[x]=0;
    return rest;
}
int main()
{
    scanf("%d",&t);
    for (int ti=1;ti<=t;ti++)
    {
      memset(ls,0,sizeof(ls));
      e=105;s=104;tot=2;num=0;
      scanf("%d",&n);
      for (int i=1;i<=n;i++)
      {
        scanf("%d",&school[i]);
        if(school[i]) addl(i+n,e,1);
        //床
      }
      for (int i=1;i<=n;i++)
      {
        scanf("%d",&home[i]);
        if (!school[i]||(school[i]&&!home[i])) 
          addl(s,i,1),num++;
          //需要匹配的人
      }
      for (int i=1;i<=n;i++)
      {
        for (int j=1;j<=n;j++)
        {
            scanf("%d",&x);
            if (x||i==j)
              addl(i,j+n,1);
              //和自己或直接朋友的床匹配
        }
      }
      ans=0;
      while (bfs()) 
        ans+=dinic(s,2147483647);
      if (ans==num) printf("^_^\n");
      else printf("T_T\n");
    }
}

小技巧

其实有些时候可以将本来不需要的在其他地方分配掉可能可以降低难度

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值