Hrbust-1492 盒子(二分图最大匹配)

21 篇文章 0 订阅
21 篇文章 0 订阅

这里写图片描述
二分图模板题。
输入N表示盒子总数,M表示关系数
接下来M行表示A盒子可以放在B盒子内部,也就是说盒子谁放在谁里的关系是一对一的。
既然要求放在外面的盒子最少,那么就应该尽量使每个盒子都尽量找到一个比自己大的并且放进去。
又因为不能有超过两个盒子并列放在一个盒子内部,也就是说这种找比自己体积大的盒子容量的关系是一对一的。
简单来说就是某个盒子只能配对一个比自己大的,并且保证尽量每个盒子都能找到!
这就是一个二分图最大匹配问题。和找男女朋友一样的。
匈牙利算法,有机会就上,没有机会创造机会也要上!

#include<stdio.h>///二分图最大匹配
#include<string.h>
int match[505];
bool edge[505][505],vis[505];///edge存图,表示在i是否能装下j盒子,vis标记是否询问过某个点
int n,k;
bool KM(int x)///DFS求增广路(求每个盒子所能对应的匹配,能被谁装进去)
{
    for(int i=1;i<=n;i++)///遍历能装下x编号的盒子
    {
        if(!vis[i]&&edge[x][i])
        {
            vis[i]=true;///标记访问点
            if(match[i]==0||KM(match[i]))
            {
                match[i]=x;///更新配对关系
                return true;
            }
        }
    }
    return false;///若遍历了所有点仍未找到盒子装下,这个盒子裸露在外
}
int main()
{
    while(scanf("%d%d",&n,&k)!=EOF)
    {
        int x,y;
        memset(edge,false,sizeof(edge));
        memset(match,0,sizeof(match));
        for(int i=0;i<k;i++)
        {
            scanf("%d%d",&x,&y);
            edge[x][y]=true;///这里x盒子能被y盒子装下,不表示y盒子能被x盒子装下,也就是有向图
        }
        int sum=0;
        for(int i=1;i<=n;i++)
        {
            memset(vis,false,sizeof(vis));
            KM(i);
        }
        for(int i=1;i<=n;i++)if(match[i]==0)sum++;///最后计数没有被装下,没有配对的盒子个数
        printf("%d\n",sum);
    }
}

match数组用于记录配对关系,也就是match[i]表示第i号盒子装了几号盒子。一开始大家都是空的用0表示,然后DFS为每个盒子配对,通过遍历能装下自己的盒子来搜索能和自己配对的盒子(不是能装下就一定配对了的)。对于每个盒子i,我们询问所有能装下自己的盒子,询问过的盒子做标记。表示这个盒子问过了,我已经为其找到了配对,而对于一个当前盒子中没有询问过的盒子,我将问他能不能装我,一种情况是,他谁都没装,那装我没问题!直接配对,第二种情况,他已经装了别的盒子,那么我让这个别的盒子再去寻找其他能装别的盒子的盒子,如果找到了,我将被我问的这个盒子装进去。
这个vis询问标记是对于每个盒子而言的,也就是说我1号盒子问过了2号盒子,那么1将不再问2(废话),即使间接的也不能再问(间接是指在DFS过程中可能会让别的盒子再去问2),因为这样会无限递归。
而对于盒子3,3没问过2,就可以询问。
最后统计match中为0盒子个数,也就是没有装盒子的盒子,这就是最终没有被装的盒子的数量。(想想,层层包裹的盒子作为一个集合,这个盒子中只有一个没装盒子,也只有一个没被盒子装,没装盒子的盒子match为0,根据这个特点找到0的个数,即是集合的个数,也是裸露在外的盒子个数)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值