【SinGuLaRiTY-1019】 Copyright (c) SinGuLaRiTy 2017. All Rights Reserved.

二分图

二分图是图论中一种特殊的图形。顾名思义,二分图G可以被分为X、Y两个子图,在二分图中,没有任意一条连线的两个端点都属于X图或Y图,即每一条连线都贯穿连接着两个子图。二分图的一个重要等价定义是:不含有「含奇数条边的环」的图。

如图-1所描绘的图像就是一个二分图,而图-2就不是一个二分图。

                    

图-1                                             图-2

匹配

在图论中,一个匹配是一个边的集合,其中任意两条边都没有公共顶点。

如图-3中的红色边就是图-1的一种匹配(显然,匹配可能不止一种)

图-3

相关的定义还有匹配点、匹配边、未匹配点、非匹配边,它们的含义也就显而易见了。

最大匹配

一个图所有匹配中,所含匹配边数最多的匹配,称为这个图的最大匹配。图 -4 是一个最大匹配。

完美匹配

如果一个图的某个匹配中,所有的顶点都是匹配点,那么它就是一个完美匹配。图 -4 是一个完美匹配。显然,完美匹配一定是最大匹配(完美匹配的任何一个点都已经匹配,添加一条新的匹配边一定会与已有的匹配边冲突)。但并非每个图都存在完美匹配。

图-4

下面我们借用一个例子来看看这两种匹配的区别:(Tips: 这个例子摘自其它博客,但确实很形象)

如下图所示,如果在某一对男孩和女孩之间存在相连的边,就意味着他们彼此喜欢。是否可能让所有男孩和女孩两两配对,使得每对儿都互相喜欢呢?图论中,这就是完美匹配问题。如果换一个说法:最多有多少互相喜欢的男孩/女孩可以配对儿?这就是最大匹配问题。

匈牙利算法的相关概念

交替路

从一个未匹配点出发,依次经过非匹配边、匹配边、非匹配边...形成的路径叫交替路 (比较形象)。

增广路:从一个未匹配点出发,走交替路,如果途径另一个未匹配点(出发的点不算),则这条交替路称为增广路。例如,图- 5 中的一条增广路如图 -6 所示(图中的匹配点均用红色标出)

                                     

图-5                                                                                                   图-6

增广路有一个重要特点:非匹配边比匹配边多一条。因此,研究增广路的意义是改进匹配,只要把增广路中的匹配边和非匹配边的身份交换即可。由于中间的匹配节点不存在其他相连的匹配边,所以这样做不会破坏匹配的性质。交换后,图中的匹配边数目比原来多了 1 条。

我们可以通过不停地找增广路来增加匹配中的匹配边和匹配点。找不到增广路时,达到最大匹配(这是增广路定理)。匈牙利算法正是这么做的。

匈牙利树

(Tips: 这一段的图画起来实在是太复杂了,故复制了pi9nc的图片)

匈牙利树一般由 BFS 构造(类似于 BFS 树)。从一个未匹配点出发运行 BFS(唯一的限制是,必须走交替路),直到不能再扩展为止。例如,由图Fig.7,可以得到如Fig.8 的一棵 BFS 树:

       

这棵树存在一个叶子节点为非匹配点(7 号),但是匈牙利树要求所有叶子节点均为匹配点,因此这不是一棵匈牙利树。如果原图中根本不含 7 号节点,那么从 2 号节点出发就会得到一棵匈牙利树。这种情况如Fig.9 所示(顺便说一句,Fig.8 中根节点 2 到非匹配叶子节点 7 显然是一条增广路,沿这条增广路扩充后将得到一个完美匹配)。

匈牙利算法的思路

用增广路求最大匹配(称作匈牙利算法,匈牙利数学家Edmonds于1965年提出) 算法轮廓:

(1)置M为空

(2)找出一条增广路径P,通过取反操作获得更大的匹配M’代替M

(3)重复(2)操作直到找不出增广路径为止

<伪代码>

bool dfs(int k)  //寻找从k出发的对应项的可增广路
{
while (从邻接表中列举k能关联到顶点j)
{
if (j不在增广路上)
{
把j加入增广路;
if (j是未盖点 或者 从j的对应项出发有可增广路)
{
修改j的对应项为k;
从k的对应项有可增广路,返回true;
}
}
}
从k的对应项出没有可增广路,返回false;
}
int hungary() //匈牙利算法主函数
{
for i = to n do//左部顶点数
{
清标志数组//右部
if (从I有可增广路)
匹配数++;
}
return 匹配数;
}
/*
[说明]

算法的核心是 dfs(int k)函数,它的作用是:

寻找顶点k可能匹配的顶点。对于每个可以与k匹配的顶点j,假如它未被匹配,可以直接使用j与k匹配;

假如j已与某顶点x匹配,那么只需调用dfs(x)来求证x是否可以与其它顶点匹配,如果返回true的话,仍可以使j与k匹配,这就是一次dfs;

每次dfs时,要标记访问到的顶点(visit[j]=true),以防死循环和重复计算;

每次dfs开始前所有的顶点都是未标记的。 主过程只需对每个左侧的顶点调用dfs,如果返回一次true,就对最大匹配数加一;一个简单的循环就求出了最大匹配的数目。

*/

实现代码

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<iostream> #define MAXN 110 //最大顶点数 using namespace std; bool Map[MAXN][MAXN]; //二分图结构-邻接矩阵存储,有向图
bool visit[MAXN]; //标志数组,标记Y集合中的 i 元素是否被检测到过
int match[MAXN]; //存储匹配的方案,Y集合中的 i 元素匹配 X 中的某个元素
int n,m; //n, m 分别为X集合与Y集合的顶点数 bool dfs(int k)
{
int i,t;
for(i=;i<=m;i++)
if(Map[k][i]&&!visit[i])
{
visit[i]=true;
if( match[i]==||dfs(match[i]))
{
match[i]=k;
return true;
}
}
return false;
}
int hungary()
{
int i,ans;
ans=;
for(i=;i<=m;i++)
match[i]=;
for(i=;i<=n;i++)
{
memset(visit,,sizeof(visit));
if(dfs(i))
ans++;
}
return ans;
}
int main()
{
int i,j,k;
scanf("%d%d",&n,&m);
for(i=;i<=n;i++)
for(j=;j<=m;j++)
{
scanf("%d",&k);
if(k==)
Map[i][j]=true;
else
Map[i][j]=false;
}
printf("%d",hungary());
return ;
}

Time: 2017-07-05

[SinGuLaRiTy] 二分图&匈牙利算法的更多相关文章

  1. hiho1122_二分图匈牙利算法

    题目 给定一个图的N个节点和节点之间的M条边,数据保证该图可以构成一个二分图.求该二分图最大匹配. 题目链接:二分图最大匹配     首先通过染色法,将图的N个节点分成两个部分:然后通过匈牙利算法求二 ...

  2. hdu-1150(二分图+匈牙利算法)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1150 思路:题目中给出两个机器A,B:给出k个任务,每个任务可以由A的x状态或者B的y状态来完成. 完 ...

  3. POJ 3020 Antenna Placement(二分图 匈牙利算法)

    题目网址:  http://poj.org/problem?id=3020 题意: 用椭圆形去覆盖给出所有环(即图上的小圆点),有两种类型的椭圆形,左右朝向和上下朝向的,一个椭圆形最多可以覆盖相邻的两 ...

  4. cogs 886. [USACO 4.2] 完美的牛栏 二分图 匈牙利算法

    886. [USACO 4.2] 完美的牛栏 ★★☆   输入文件:stall4.in   输出文件:stall4.out   简单对比时间限制:1 s   内存限制:128 MB USACO/sta ...

  5. POJ 3041 Asteroids(二分图 && 匈牙利算法 && 最小点覆盖)

    嗯... 题目链接:http://poj.org/problem?id=3041 这道题的思想比较奇特: 把x坐标.y坐标分别看成是二分图两边的点,如果(x,y)上有行星,则将(x,y)之间连一条边, ...

  6. HDU - 2444 二分图最大匹配 之 判断二分图+匈牙利算法

    题意:第一行给出数字n个学生,m条关系,关系表示a与b认识,判断给定数据是否可以构成二分图,如果可以,要两个互相认识的人住一个房间,问最大匹配数(也就是房间需要的最小数量) 思路:要看是否可以构成二分 ...

  7. POJ1325机器重启次数——二分图匈牙利算法模板

    题目:http://poj.org/problem?id=1325 求最小点覆盖.输出最大匹配数就行,结果略复杂地弄了. 注意由题可知 可以直接把与0有关的边删掉.不过亲测不删0而计数时不计0就会WA ...

  8. UESTC 919 SOUND OF DESTINY --二分图最大匹配+匈牙利算法

    二分图最大匹配的匈牙利算法模板题. 由题目易知,需求二分图的最大匹配数,采取匈牙利算法,并采用邻接表来存储边,用邻接矩阵会超时,因为邻接表复杂度O(nm),而邻接矩阵最坏情况下复杂度可达O(n^3). ...

  9. HDU5090--Game with Pearls 二分图匹配 (匈牙利算法)

    题意:给N个容器,每个容器里有一定数目的珍珠,现在Jerry开始在管子上面再放一些珍珠,放上的珍珠数必须是K的倍数,可以不放.最后将容器排序,如果可以做到第i个容器上面有i个珍珠,则Jerry胜出,反 ...

随机推荐

  1. 在ThinkPHP的common.php文件里添加公共函数的注意事项

    注意事项: 1.函数不要加public访问控制权限,因为默认就是public的. 2.当你写好了一个新函数后在本地运行发现没有问题,但是在生产环境运行会报错:找不到这个函数,解决方法是删除runtim ...

  2. NET代码运行在服务器JS运行在客户端

    using System;using System.Collections.Generic;using System.IO;using System.Linq;using System.Web;usi ...

  3. FFmpeg结构体:AVInputFormat

    1.描述 AVInputFormat 是类似COM 接口的数据结构,表示输入文件容器格式,着重于功能函数,一种文件容器格式对应一个AVInputFormat 结构,在程序运行时有多个实例,位于avof ...

  4. DAY19-Django之model进阶

    QuerySet 可切片 使用Python 的切片语法来限制查询集记录的数目 .它等同于SQL 的LIMIT 和OFFSET 子句. >>> Entry.objects.all()[ ...

  5. Java Swing 如何让窗体居中显示

    如题,其他不多说,直接上代码! package com.himarking.tool; import java.awt.Toolkit; import javax.swing.JFrame; @Sup ...

  6. form表单提交target属性使用

    通过form表单提交刷新iframe <form action="doctor/selPackage" target="projectlistframe" ...

  7. C++中cin.get(),cin.getline(),cin>>,gets(),cin.clear()使用总结

    1.cin.get()  实质:类istream所定义对象cin的重载成员函数 用于读取单字符  istream& get(char&)    int get(void) 用于读取字符 ...

  8. 洛谷P2146 树链剖分

    题意 思路:直接树链剖分,用线段树维护即可,算是树剖的经典题目吧. 代码: #include <bits/stdc++.h> #define ls(x) (x << 1) #d ...

  9. eclips git中的add to Index无效解决

    今天在使用eclips git中的add to Index,发现其无效,具体如下 问题描述: 通过export导入一个git java项目 在java工程中新增一个类文件IndicatorCalcTe ...

  10. JS中的引用类型

    JS的数据类型可以分为两类:一类是原始类型(比如数字.布尔值.字符串.undefined.null),另外就是对象类型.我们通常将对象类型称为引用类型.对象值都是引用.举个例子来说明,下如下的代码: ...