二分匹配算法初探(基于dfs)--Machine Schedule(from杭电oj)

二分匹配算法(binary search).此算法的学习有几个基础知识需要了解,dfs,邻接矩阵,二分图等.dfs,邻接矩阵我在之前的博客有记录过:

dfs:

https://blog.csdn.net/qq_49593247/article/details/116457624?spm=1001.2014.3001.5501

邻接矩阵:

https://blog.csdn.net/qq_49593247/article/details/116568486?spm=1001.2014.3001.5501

我在记忆一下二分图.抽象的讲,二分图就是两个不同的点集,两个集合之间各点有所联系,但是同点集没有联系,如下图:

 

这就是二分图,二分匹配算法就是针对与二分图使用dfs实现的.

具体拿看例子记录算法思想.


问题描述

众所周知,机器调度是计算机科学中的一个非常经典的问题,并且已经有很长的历史了。 调度问题在必须满足的约束的性质和所需的调度类型方面差异很大。 在这里,我们考虑一个两机调度问题。

机器A和B有两个。机器A有n种工作模式,称为mode_0,mode_1,…,mode_n-1,同样,机器B也有m种工作模式,mode_0,mode_1,…,mode_m-1。 一开始它们都在mode_0下工作。

对于给定的k个作业,可以在特定模式下在两台机器中的任何一台中对其进行处理。 例如,可以在机器A的mode_3或B的机器在mode_4处处理作业0,可以在机器A的mode_2或在机器B的mode_4处处理作业1,依此类推。 因此,对于作业i,约束可以表示为三元组(i,x,y),这意味着可以在机器A中以mode_x或在机器B中以mode_y对其进行处理。

显然,要完成所有工作,我们需要不时更改机器的工作模式,但是不幸的是,只能通过手动重新启动机器来更改机器的工作模式。 通过更改作业顺序并将每个作业分配给合适的计算机,请编写程序以最大程度地减少重新启动计算机的时间。

输入

该程序的输入文件由几种配置组成。 一个配置的第一行包含三个正整数:n,m(n,m <100)和k(k <1000)。 以下k行给出了k个作业的约束,每行是一个三元组:i,x,y。

输入将由包含单个零的行终止。

输出

输出应该是每行一个整数,这意味着重新启动计算机的最少时间。

样本输入

5 5 10

0 1 1

1 1 2

2 1 3

3 1 4

4 2 1

5 2 2

6 2 3

7 2 4

8 3 3

9 4 3

0

样本输出

3


首先要读懂题目,两个机器,在不同模式运行不同任务,可以把这个化为二分图,当机器处于一种机器A模式x的时候,可以按照任务列表,同步完成所有A模式为x可完成的任务(不论它的在机器B完成需要什么模式,我们就在A处理),同理,取覆盖面最大的一个模式.我们可以把上题例子转换为二分图:

这里二分图转换有个易错点,虽说有五种模式,但是未启动前,A,B都有一个0模式,直接启动转换第一种模式.

然后就是针对题目分析,要完成所有任务,并且转换最少次模式,上图左右两边点分别是A,B的模式,相连的线其实就是任务,有几根线就有几个任务,要想实现所有任务全部完成,就是在左右两边选点,在这个点出发的线就会被覆盖,这个模式下可以完成的任务就会被完成,那么只要找到最少的点覆盖全部全部线就是最少需要多少次模式转换.那么如何求这个点数呢,我们发现,上图由A1,A2,B3三个点覆盖了所有的线,所以,除了他们三点外其他点都没有联系,那么我们可不可以理解为,如果这三个点和随机一个与其相连的点相匹配,那么剩余的点都互相没有线连着,也就是说不能在匹配,最多能在这个图中找到三对可以好的点,所以问题又可以转换,求最小点覆盖全图,其实也就是求最多可以匹配成功多少对点的问题.

对于这个最大匹配问题,我们先从遍历每一个左边的点,如果它和右边的一个点相连接,那么就将这两个点相匹配,找到了就进行下一步,找下一个左边的点,看它有没有可以匹配的点,有就匹配.当然,这是最理想的情况?如果我们遍历左边点时,发现可以和它匹配的点已经被匹配,那么我们需要考虑已经被匹配的那个左边的点还有没有其他相连接可以匹配的点,如果有就匹配,如果没有,这里两个左边点就只能匹配一个.这个时候就要采用dfs算法去搜索他们的匹配情况了.

我的代码如下:

#include<iostream>
#include<cstring>
using namespace std;
#define max 507
int map[max][max];//邻接矩阵存图
int linker[max];//储存与右边点相匹配的左点,下标为右点的序号
int used[max];//右边点是否被搜索
int res=0;//记录一共几对
int lefT,righ;//记录A,B机器的模式种数
int dfs(int lef){
	for(int u=0;u<righ;u++){//在每个左点条件下搜索右点
		if(used[u]!=1&&map[lef][u]!=0){//如果此时右边点没有被搜索并且左边点和它想连
			used[u]=1;//标记已经被搜索
			if(linker[u]==-1||dfs(linker[u])){//如果该右点没有左点与只匹配,或者如果该右点有与之相匹配的左点,那么我们用dfs搜索它的匹配左点有没有其他可以匹配的右点
				linker[u]=lef;//满足上述情况就进行匹配
				return 1;//匹配成功返回1
			}
		}
	}
	return 0;//如果遍历完都没有找到与左点相匹配的点,返回0
}
int hur(){
	memset(linker,-1,sizeof(linker));
	for(int i=0;i<lefT;i++){//把左边点全部遍历一次
		memset(used,-1,sizeof(used));//对左边每个点进行遍历时,因为进行递归,要保证这个点有没有被搜索,被搜索了调用时就不用判断该点了
		if(dfs(i)==1)res++;
	}
	return res;//返回匹配成功对数
}
int main(){
	int k,num,qwq,owo;//n,m分别为A,B机器的模式种类,一共k组数据
	while(scanf("%d",&lefT)==1&&lefT!=0){//等于0程序结束
	scanf("%d%d",&righ,&k);
	res=0;
	memset(map,0,sizeof(map));//初始化邻接矩阵
	while(k--){
		scanf("%d%d%d",&num,&qwq,&owo);//num标序号,没有实用
		if(qwq!=0&&owo!=0)
			map[qwq][owo]=1;//储存任务
	}//初始化邻接矩阵
	printf("%d\n",hur());//输出
	}
}

代码意义可看注释.是根据上文的思路写出来的.图论问题,任重而道远.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值