过山车(匈牙利算法)- HDU 2063

Problem Description

RPG girls今天和大家一起去游乐场玩,终于可以坐上梦寐以求的过山车了。可是,过山车的每一排只有两个座位,而且还有条不成文的规矩,就是每个女生必须找个个男生做partner和她同坐。但是,每个女孩都有各自的想法,举个例子把,Rabbit只愿意和XHD或PQK做partner,Grass只愿意和linle或LL做partner,PrincessSnow愿意和水域浪子或伪酷儿做partner。考虑到经费问题,boss刘决定只让找到partner的人去坐过山车,其他的人,嘿嘿,就站在下面看着吧。聪明的Acmer,你可以帮忙算算最多有多少对组合可以坐上过山车吗?

Input

输入数据的第一行是三个整数K , M , N,分别表示可能的组合数目,女生的人数,男生的人数。0<K<=1000 1<=N 和M<=500.接下来的K行,每行有两个数,分别表示女生Ai愿意和男生Bj做partner。最后一个0结束输入。

Output

对于每组数据,输出一个整数,表示可以坐上过山车的最多组合数。

Sample Input

6 3 3

Sample Output

3

匈牙利算法:

算法的核心就是根据一个初始匹配不停的找增广路,直到没有增广路为止。

匈牙利算法的本质实际上和基于增广路特性的最大流算法还是相似的,只需要注意两点:

(一)每个X节点都最多做一次增广路的起点;

(二)如果一个Y节点已经匹配了,那么增广路到这儿的时候唯一的路径是走到Y节点的匹配点

找增广路的时候既可以采用dfs也可以采用bfs,两者都可以保证O(nm)的复杂度,因为每找一条增广路的复杂度是O(m),而最多增广n次,dfs在实际实现中更加简短。

匈牙利算法的基本模式:

1、 初始时最大匹配为空

2、 while (找得到增广路径)

3、 do 把增广路径加入到最大匹配中。

如果二分图的左半边一共有n个点,最多找n条增广路径,如果图中有m条边,每一条增广路径把所有边遍历一遍,所以时间复杂度为O(n*m);

算法思想:

算法的思路是不停的找增广轨, 并增加匹配的个数,增广轨顾名思义是指一条可以使匹配数变多的路径,在匹配问题中,增广轨的表现形式是一条"交错轨",也就 是说这条由图的边组成的路径, 它的第一条边是目前还没有参与匹配的,第二条边参与了匹配,第三条边没有..最后一条边没有参与匹配,并且始点和终点还没 有被选择过.这样交错进行,显然 他有奇数条边.那么对于这样一条路径,我们可以将第一条边改为已匹配,第二条边改为未匹配...以此类推.也就是将所有 的边进行"反色",容易发现这样修 改以后,匹配仍然是合法的,但是匹配数增加了一对.另外,单独的一条连接两个未匹配点的边显然也是交错轨.可以证明, 当不能再找到增广轨时,就得到了一个 最大匹配.这也就是匈牙利算法的思路。

二分图匹配中较为重要的三个公式:

二分图最小顶点覆盖 = 二分图最大匹配;

DAG图的最小路径覆盖 = 节点数(n)- 最大匹配数;

二分图最大独立集 = 节点数(n)- 最大匹配数;

源代码:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=1e3+10;
int n1,n2,k;
//n1,n2为二分图的顶点集,其中x属于n1,y属于n2
int map[maxn][maxn],vis[maxn],link[maxn];
//link记录n2中的点y在n1中所匹配的x点的编号
void init()
{
    memset(map,0,sizeof map);
    memset(link,0,sizeof link);
}
int find(int x)
{
    for(int i=1;i<=n2;i++)
    {
        if(map[x][i]&&!vis[i])//x->i有边,且节点i未被搜索
        {
            vis[i]=1;//标记节点已经被搜索
            //如果i不属于前一个匹配M,或被i匹配到的节点可以寻找到增广路
            if(link[i]==0||find(link[i]))
            {
                link[i]=x;//更新
                return 1;//匹配成功
            }
        }
    }
    return 0;
}
int main()
{
    while(~scanf("%d",&k)&&k){
        scanf("%d%d",&n1,&n2);
        init();
        for(int i=0;i<k;i++)
        {
             int x,y;
            scanf("%d%d",&x,&y);
            map[x][y]=1;
        }
        int ans=0;
        for(int i=1;i<=n1;i++)
        {
            memset(vis,0,sizeof vis);
            if(find(i)) ans++;
        }
        printf("%d\n",ans);
    }
   return 0;
}

本文分享自微信公众号 - ACM算法日常(acm-clan)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-09-02

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏JAVA高级架构

七夕情人节,程序员怎样表白更有效?

七夕情人节快乐 2017.08.28 今天是传统节日--七夕节,也是中国人传统意义上的"情人节",在此祝大家开心。然后,各大平台又被七夕节刷屏了... 作为国...

1.3K60
来自专栏码代码的陈同学

异常处理的反模式

异常设计实践 中有位读者评论:又记录日志又抛异常反模式设计。其实我并不知道反模式,本文是对于反模式的学习整理,数据都来自参考资料。

19650
来自专栏YoungGy

ML基石_8_NoiseAndError

recap Noise and Probabilistic Target noise来源 Probabilistic Target Error Measure ...

21150
来自专栏C语言及其他语言

如何到达C语言的巅峰?我推荐你阅读《C语言小白变怪兽》!

《C语言小白变怪兽》融入了作者 8 年的编程功力,以及文学级的写作能力,耗时 5 年完成,期间经过了 5 次大改版。

4K20
来自专栏程序员宝库

如何让你的代码整洁漂亮?

Robert Martin的这句话非常合适: “唯一能有效测量代码质量的方式是每分钟说多少个What-the-Fk ”** 让我深入解释一下: 做代码回顾的时...

35260
来自专栏青玉伏案

设计模式(一):“穿越火线”中的“策略模式”(Strategy Pattern)

在前段时间呢陆陆续续的更新了一系列关于重构的文章。在重构我们既有的代码时,往往会用到设计模式。在之前重构系列的博客中,我们在重构时用到了“工厂模式”、“策略模式...

23760
来自专栏老付的网络博客

装饰者模式

在23种设计模式中,装饰者模式在游戏开发的过程中,使用的很是频繁。因为这个设计模式,把所有的业务的逻辑封装的对应的实体类中,从而为主流程减负了。首先看下一个应用...

34540
来自专栏Java技术栈

3 年 Java 应该具备的技能体系

一名3年工作经验的Java程序员应该具备的技能,这可能是Java程序员们比较关心的内容。

11530
来自专栏不二小段

【爬虫与反爬】记一次网址编码研究

相爱相杀的爬虫与反爬工程师啊……愿你们和谐相处。 前些日子写爬虫时遇到一个比较奇怪的编码,是构造目标网址的一个组成部分,我更倾向于说是编码而不是加密,虽然的确有...

35980
来自专栏开源FPGA

数字电路基础

十进制数转化为R进制数:整数部分,除R取余法,除到商为0为止。小数部分,乘R取整法,乘到积为0为止。

14510

扫码关注云+社区

领取腾讯云代金券