匈牙利算法的粗略解释

匈牙利算法的应用场景:二分图

可以理解分为两个集合,集合内部的元素不进行彼此配对,但是两集合之间进行配对

A集合与B集合进行配对,二分图如下

以上即为二分图 ,其中A集合与B集合进行配对,这个图可以刨析为三个部分,A集合为一部分,B集合为一个部分,连接A集合和B集合的线是另一部分,总共这三个部分,如果用代码实现就可以记录这三个部分

连接AB集合的线可以视为对于AB集合的关系

此时带入一个情景

假设你是月老,此时需要对一群男女进行牵红线,但是需要考虑男女之间的意愿,输入的K代表女生所心仪的男生(也就是关系的数量),直到输入0之后停止,M为女生的数量,N代表男生的数量,而你作为月老所需要做的就是尽可能多的对男女进行配对

#include <stdio.h>
bool G[510], B[510], relate[510][510];
//此处的G用来存储女生是否已经有配对的伴侣
//B用来存储男生是否已经被配对
//relate用来记录女生对于男生的心仪情况
//G和B相当于AB集合,而relate就相当于AB之间的关系
int K, M, N;
int main()
{
	scanf("%d %d %d", &K, &M, &N);
	int girl = 0, boy = 0;
	int num = 0;
	while (K != -1)
	{
		K--;
        //表示输入关系的数量,每输入一次就对K进行-1
		scanf("%d", &girl);
		if (girl == 0)
		{
			break;
		}
		scanf("%d", &boy);
		relate[girl][boy] = true;
        //以女生为行,男生为列,相关关系默认为flase,当输入关系里面有时,更改值成为false
		if (G[girl] == false)
		{
			if (B[boy] == false)
			{
                //当女生没有伴侣,男生也没有伴侣的时候就可以直接将他俩牵起来
                //此时需要更改的是男生和女生的配偶状态
				B[boy] = true;
				G[girl] = true;
				num++;
                //此时算作是已经是配对成功一对,所以num++
			}
			else
			{
                //如果男生1已经暂定配对了一个伴侣
                //此时将这个男生1的伴侣先找出来,并检查他的伴侣是否还有其它心仪的男生
                //如果这个女生1有其他心仪的对象,且这个男生2也没有伴侣
                //那么就将这个男生2配对给这个女生1,boy所记录男生就可以配给girl记录的女生
                //如果这个男生1的伴侣没有其他心仪的对象,或者其他心仪对象都有伴侣
                //那么没有办法配对了,所以num不进行操作
				for (int i = 1; i <= M; i++)
				{
					if (relate[i][boy] == true && G[i] == true)
					{
						for (int k = 1; k <= N; k++)
						{
							if (relate[i][k] == true && B[k] == false && boy != k)
							{
								G[girl] = true;
								B[k] = true;
								num++;
								break;
							}
						}
					}
				}
			}
		}
	}
	printf("%d", num);
}

粗略的复述一下这个想法

就是G集合和B集合分别表示女生和男生的配偶情况,如果是G或者B有了对象,就让G和B所对应的值改为true,如图所示

 而对于实行配对的逻辑就是

 而过山车这道题也可以用这种方式进行解决

 

#include <stdio.h>
#include <string.h>
int map[1000][1000] = {0};
//map记录两个集合之间的关系
int	boy[500] = {0};
int	flag[500] = {0};
int n, a, b, M, N, num;

int fun(int z);

int main() {	
	while (scanf("%d", &n) != EOF && n)
	{
		num = 0;
		memset(map, 0, sizeof(map));
		memset(boy, 0, sizeof(boy));
        //对两个数组进行初始化,初始化值为0
		scanf("%d %d", &M, &N);
		while (n--)
		{
			scanf("%d %d", &a, &b);
			map[a][b] = 1;
            //此处的循环是为了记录所有的关系
		}
		for (int i = 1; i <=M; i++)
		{
			memset(flag, 0, sizeof(flag));
            //每循环一次,就将flag数组的值重新进行初始化
			num += fun(i);
            //这一步就是配对步骤,由fun函数对于关系进行判断
            //i依旧是代表女生,传入相当于对没一个女生进行配对
		}
		printf("%d\n", num);
		
	}
	return 0;
}
int fun(int z) {
	for (int k = 1; k <= N; k++)
	{
        //此处的k是在关系网之中的男生
		if (map[z][k] == 1 && flag[k] ==0)
        //判断条件为筛选有关系的z,k且k没有被访问过
		{	
			flag[k] = 1;
            //一旦进入该语句,证明k已经被访问过了,所以更改k的值
			if (boy[k] == 0 || fun(boy[k]) )
			{
				boy[k] = z;
				return 1;
			} 
            //此处判断语句,分为两种情况
                //第一种情况:k所对应的男生没有配对对象,这种情况下直接对二者进行配对
                //第二种情况:k所对应的男生有配对对象,flag已经对此次访问过的对象进行
                //了标记,所以再次进行调用,对除了目前这个男生的配对对象进行再次配对,
                //一直到寻找到目标男生,或者找不到为止
			
		}
	}
	return 0;
}

以上是另一种实现方法,此时用boy这个数组来记录的是与之配对的女生的编号,flag在每一次寻找配对对象的过程中避免多次访问,且在函数进行递归的过程中,能够有效的避免再次访问相同的男生,以找到相应的男生。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值