一、什么是二分图?
如果一个图的顶点可以分为两个集合X和Y,图的所有边一定是有一个顶点属于集合X,另一个顶点属于集合Y,则称该图为“二分图”或“二部图”
二分图的应用中,最常见的就是求—最大匹配,很多其他的问题都可以转化为匹配问题来解决。
怎么求二分图的最大匹配?----这里用到了匈牙利算法
下面看一个简单的例子:
比如男女搭配问题,上边的顶点是男生,下边的顶点是女生,有连线的两个点是可以配对的,现在要求的是最大配对数
- 首先把男生放在左边,从男生第一个开始寻找可以匹配的女生,这里就是男1->女1,然后再找第二个男生,发现第二个男生也是和第一个女生匹配的,但是第一个女生已经被第一个男生匹配走了,这时就到了匈牙利算法的关键:如果和男2匹配的女生已经被匹配走,那么就找和该女生对应的男生(男1),看该男生是否可以和其他女生匹配,如果可以那么匹配数就加1,如果不可以,就不加
- 从算法角度来看就是,对每个男生进行一次DFS搜索,看右边是否有可以匹配但未匹配的女生,如果该女生已经匹配,就找(与其匹配的)对应的男生,看该男生是否可以找到一个未匹配的女生,以此类推下去,直到找到可以匹配的女生为止,该算法也就是进行了n次DFS(n为男生数量)
二、 二分图的最小顶点覆盖
在二分图中求最少的点,让每条边都至少和其中的一个点关联,这就是二分图的最小顶点覆盖,(最小定点覆盖就是用最少的点覆盖所有的边)
还看这个例子:
为了禁止男女生谈恋爱,学校需要开除学生。现在要求的是最少开除多少学生可以避免这种现象,答案是:开除女1女2男4,因为这三个点覆盖了所有的边
那么如何求最小顶点覆盖呢?先来看一下,最多匹配几对?也是三对。可以发现,如果匹配一对,比如男4匹配了,那么其余可以和他匹配的女生都不能再匹配了,也就是被删除了,这三个点都匹配后,其余所有点也就都被删除了
结论:二分图的最小定点覆盖数=二分图的最大匹配数
三、 DAG图的最小路径覆盖
(有向无环图)用尽量少的不相交简单路径覆盖有向无环图的所有顶点,这就是DAG图的最小路径覆盖问题(最小路径覆盖就是用最少的路覆盖所有的点)
如果有n个点,匹配数为0,那么就需要n条边,没匹配一对,就会减少一条边。
DAG图最小覆盖数=节点数-最大匹配数
四、二分图的最大独立集
为了使尽量多的学生参加活动,又不能让他们相互能够匹配,那么最多选多少学生,(和开除学生一样)可以带10个人去,除了开除的三个人
二分图的最大独立集数=节点数-最大匹配数
五、POJ—1325—Machine Schedule
思路:
这道题其实就是二分图的最小顶点覆盖问题,问的是最少重启机器次数,也就是用最少的点去覆盖全部的边。下边代码有详细注释
代码:
#include <iostream>
#include <cstring>
using namespace std;
const int MAXN=510;
int Left,Right;//left是匹配左边的顶点数,right是匹配右边的顶点数
int g[MAXN][MAXN];//邻接矩阵
int linker[MAXN];//存右点对象(linker[i]表示和女生i匹配的男生的编号)
int vis[MAXN];//右点是否访问过
int dfs(int l)
{
for(int r=0;r<Right;r++)
{
if(g[l][r]&&!vis[r])//左边到右边有边,并且右边该点未访问
{
vis[r]=1;
if(linker[r]==-1||dfs(linker[r]))
//如果右边的点还未匹配 或者 与该点匹配的左边的点可以找到另一个右点与其匹配,那么返回1
//该女生没有对象 或者 该女生的男朋友可以找到另一个对象 该女生就和该男生匹配,匹配数加一
{
linker[r]=l;
return 1;
}
}
}
return 0;
}
//匈牙利算法
int hungary()
{
int res=0;
memset(linker,-1,sizeof(linker));
for(int l=0;l<Left;l++)//n趟dfs
{
memset(vis,0,sizeof(vis));//防止单次dfs重复搜
if(dfs(l)) res++;
}
return res;
}
int main()
{
int k;
while(cin>>Left&&Left)
{
cin>>Right>>k;
memset(g,0,sizeof(g));
int id,lnum,rnum;
while(k--)
{
cin>>id>>lnum>>rnum;
if(lnum!=0&&rnum!=0)//等于0,不需要切换,可以直接完成
{
g[lnum][rnum]=1;
}
}
cout<<hungary()<<endl;
}
return 0;
}