POJ---1325---Machine Schedule---二分匹配算法

一、什么是二分图?

如果一个图的顶点可以分为两个集合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

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;
}

猜你喜欢

转载自blog.csdn.net/weixin_45102820/article/details/114953851