匈牙利算法

二分图:把一个图的顶点划分为两个不相交集 U  和 V ,使得每一条边都分别连接U 、 V  中的顶点。如果存在这样的划分,则此图为一个二分图。

匹配:在图论中,一个「匹配」(matching)是一个边的集合,其中任意两条边都没有公共顶点。例如,图 3、图 4 中红色的边就是图 2 的匹配。

      

匹配点匹配边未匹配点非匹配边:例如图 3 中 1、4、5、7 为匹配点,其他顶点为未匹配点;1-5、4-7为匹配边,其他边为非匹配边。

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

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

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

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

代码:

 #include<iostream>
#include<cstdio>
#include<cstring>
using namespace std; const int MaxN=; int N,M;
int Ans;
int link[MaxN];
bool cover[MaxN];
bool Map[MaxN][MaxN]; void init()
{
int i,x,y;
scanf("%d%d",&N,&M);
memset(Map,false,sizeof(Map));
for(i=;i<=M;++i)
{
scanf("%d%d",&x,&y);
Map[x][y]=true;
}
}
bool Find(int i)
{
int j;
for(j=;j<=N;++j)
if(Map[i][j] && !cover[j])
{
cover[j]=true;
if(!link[j] || Find(link[j]))
{
link[j]=i;
return true;
}
}
return false;
}
void solve()
{
int i;
memset(link,,sizeof(link));
for(i=;i<=N;++i)
{
memset(cover,false,sizeof(cover));
Find(i);
}
}
void print()
{
int i;
Ans=;
for(i=;i<=N;++i)
if(link[i])
Ans++;
printf("%d\n",Ans);
for(i=;i<=N;++i)
if(link[i])
printf("%d %d\n",link[i],i);
}
int main()
{
init();
solve();
print();
return ;
}

例一:二分图

给定一个图(可能为非联通图),将其二分,得到两个数组,输出其中某个数组的个数和顶点?

输入用例(二分图检测):


算法代码:

#include <stdlib.h>
#include <stdio.h>
#include <string.h> #define MAX 1001 int path[MAX][MAX] = {};
int c[MAX] = {};
int group[MAX] = {};
int count = ;
int N, E;
int value[] = {, , };
int partition(int start) //给每个点分组,存到group[]
{
for(int i = ; i < c[start]; i++)
{
int city = path[start][i];
if(group[city] != )
{
if(group[city] == group[start])return ; //相邻两个点分组一样,则返回0,不是二分图
}
else
{
group[city] = value[group[start]];
if (group[city] == )count++;
if(!partition(city))return ;
}
} return ;
} int getNoGroup(int * pt)
{
for (int i = ; i <= N; i++)
{
if (!group[i])
{
*pt = i;
return ;
}
} return ;
} int main(void)
{
freopen("input3.txt", "r", stdin);
//freopen("output.txt", "w", stdout); for(int test_case = ; test_case <= ; test_case++)
{
scanf("%d %d\n", &N, &E);
for(int i = ; i < E; i++)
{
int pt1, pt2;
scanf("%d %d", &pt1, &pt2);
path[pt1][c[pt1]++] = pt2;
path[pt2][c[pt2]++] = pt1;
} int start = ;
bool errorFlag = false; //判断是否是二分图
while(getNoGroup(&start)) //当非连通图的时候,也能遍历到
{
group[start] = ;
if(!partition(start))
{
errorFlag = true;
break;
}
} if(errorFlag)printf("#%d -1\n", test_case);
else
{
printf("#%d %d", test_case, count);
for(int i = ; i <= N; i++)if(group[i] == )printf(" %d", i);
printf("\n");
} memset(group, , MAX*);
memset(path, , MAX*MAX*);
} return ;
}

例二:圣诞礼物

给定人数和礼物数量,接下来给出每个人喜欢的礼物的编号,求最大匹配?

输入用例(二分图最大匹配):


算法代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <vector>
using namespace std; #define MAX 401 int map[MAX][MAX] = { }; //第i个人的每个礼物的编号
int c[MAX] = { }; //第i个人一共可能连多少个礼物
int check[MAX] = { }; //表示当前第i个人已经遍历过的礼物编号
int matching[MAX] = { }; //礼物和人匹配的状态 bool dfs(int u)
{
for (int i = ; i < c[u]; i++)
{
int v = map[u][i];
if (!check[v])
{
check[v] = true;
if(matching[v] == || dfs(matching[v]))
{
matching[v] = u;
matching[u] = v;
return true;
}
}
}
return false;
} int main(int argc, char** argv)
{
freopen("input.txt", "r", stdin);
int case_max;
scanf("%d\n", &case_max);
for(int case_num = ; case_num < case_max; case_num++)
{
int personCnt, giftCnt;
scanf("%d %d\n", &personCnt, &giftCnt);
for(int i = ; i <= personCnt; i++)
{
int favoriteCnt;
scanf("%d ", &favoriteCnt);
for(int j = ; j <= favoriteCnt; j++)
{
int favorite;
scanf("%d", &favorite);
//map[i].push_back(personCnt+favorite);
map[i][c[i]++] = personCnt + favorite;
map[personCnt + favorite][c[personCnt + favorite]++] = i;
}
} int result = ;
for(int i = ; i <= personCnt; i++)
{
if (matching[i] == )
{
memset(check, , sizeof(check));
if(dfs(i))result++;
}
} printf("%d\n", result); memset(map, , sizeof(int)*MAX*MAX);
}
}

Algorithm --> 二分图最大匹配的更多相关文章

  1. POJ2239 Selecting Courses(二分图最大匹配)

    题目链接 N节课,每节课在一个星期中的某一节,求最多能选几节课 好吧,想了半天没想出来,最后看了题解是二分图最大匹配,好弱 建图: 每节课 与 时间有一条边 #include <iostream ...

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

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

  3. POJ3057 Evacuation(二分图最大匹配)

    人作X部:把门按时间拆点,作Y部:如果某人能在某个时间到达某门则连边.就是个二分图最大匹配. 时间可以二分枚举,或者直接从1枚举时间然后加新边在原来的基础上进行增广. 谨记:时间是个不可忽视的维度. ...

  4. ZOJ1654 Place the Robots(二分图最大匹配)

    最大匹配也叫最大边独立集,就是无向图中能取出两两不相邻的边的最大集合. 二分图最大匹配可以用最大流来解. 如果题目没有墙,那就是一道经典的二分图最大匹配问题: 把地图上的行和列分别作为点的X部和Y部, ...

  5. HDU:过山车(二分图最大匹配)

    http://acm.hdu.edu.cn/showproblem.php?pid=2063 题意:有m个男,n个女,和 k 条边,求有多少对男女可以搭配. 思路:裸的二分图最大匹配,匈牙利算法. 枚 ...

  6. UOJ #78 二分图最大匹配

    #78. 二分图最大匹配 从前一个和谐的班级,有 nl 个是男生,有 nr 个是女生.编号分别为 1,…,nl 和 1,…,nr. 有若干个这样的条件:第 v 个男生和第 u 个女生愿意结为配偶. 请 ...

  7. 【网络流#6】POJ 3041 Asteroids 二分图最大匹配 - 《挑战程序设计竞赛》例题

    学习网络流中ing...作为初学者练习是不可少的~~~构图方法因为书上很详细了,所以就简单说一说 把光束作为图的顶点,小行星当做连接顶点的边,建图,由于 最小顶点覆盖 等于 二分图最大匹配 ,因此求二 ...

  8. [HDU] 2063 过山车(二分图最大匹配)

    题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=2063 女生为X集合,男生为Y集合,求二分图最大匹配数即可. #include<cstdio> ...

  9. [POJ] 1274 The Perfect Stall(二分图最大匹配)

    题目地址:http://poj.org/problem?id=1274 把每个奶牛ci向它喜欢的畜栏vi连边建图.那么求最大安排数就变成求二分图最大匹配数. #include<cstdio> ...

随机推荐

  1. 集成电路883和883b有什么区别

    根据用途,元器件的质量等级可分为:用于元器件生产控制.选择和采购的质量等级和用于电子设备可靠性预计的质量等级两类,两者有所区别,又相互联系. 用于元器件生产控制.选择和采购的质量等级 元器件的质量等级 ...

  2. PCI9054芯片的型号说明及购买建议

    个脚,这也是大部分人用到的:而"BI"结尾的是BGA封装的,225个脚,较少用到,对它不再多说. 这几种系列量产的时间如下: 年11月 年8月 年2月 年 年 年到2006年期间, ...

  3. JXL组件生成报表报错(二)

    JXL组件生成报表 1.具体报错如下: usage: java org.apache.catalina.startup.Catalina [ -config {pathname} ] [ -nonam ...

  4. DirectX--给视频加马赛克、字符OSD

    在虚拟摄像头(CSourcefilter)的fillbuffer中添加代码,在打码区域50*50,像素分辨率1/10: for (int i=0;i<50;i++) { for (int k=0 ...

  5. 芝麻HTTP: Scrapy小技巧-MySQL存储

    这两天上班接手,别人留下来的爬虫发现一个很好玩的 SQL脚本拼接. 只要你的Scrapy Field字段名字和 数据库字段的名字 一样.那么恭喜你你就可以拷贝这段SQL拼接脚本.进行MySQL入库处理 ...

  6. [BZOJ1207] [HNOI2004] 打鼹鼠 (dp)

    Description 鼹鼠是一种很喜欢挖洞的动物,但每过一定的时间,它还是喜欢把头探出到地面上来透透气的.根据这个特点阿Q编写了一个打鼹鼠的游戏:在一个n*n的网格中,在某些时刻鼹鼠会在某一个网格探 ...

  7. Vue-生命周期图示 注解

    根据腾讯课堂视频讲解,将官网生命周期图示进行注解,以加深印象和理解 贴一个源码示例: 注意位置和写法

  8. 【linux之进程管理,系统监控】

    一.进程管理 前台进程:一般是指占据着标准输入和/或标准输出的进程后台进程:不占据默认开启的进程都是前台进程ctrl+C 中断ctrl+z 从前台转入后台bg 后台进程编号 让其在后台运行ls -R ...

  9. 用注解的方式实现Mybatis插入数据时返回自增的主键Id

    一.背景 我们在数据库表设计的时候,一般都会在表中设计一个自增的id作为表的主键.这个id也会关联到其它表的外键. 这就要求往表中插入数据时能返回表的自增id,用这个ID去给关联表的字段赋值.下面讲一 ...

  10. UWP:记录一下这几天踩到的坑

    最近在玩微软的Desktop Bridge项目,遇到了如下几个坑: 1.文档中给的是js项目魔改的方法,其实C#项目也可以魔改加入UWP部分的,区别在于: 不用在项目文件里写<AppxGener ...