【图论】hdu-2063 过山车

本文通过一个具体的过山车配对问题,介绍了匈牙利算法的基本思想及其在解决二分图最大匹配问题中的应用。文章提供了详细的算法实现过程及代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这段时间要沉迷刷题一段时间了,就让优快云陪我一起吧!

一、题目大意

RPG girls今天和大家一起去游乐场玩,终于可以坐上梦寐以求的过山车了。可是,过山车的每一排只有两个座位,而且还有条不成文的规矩,就是每个女生必须找个个男生做partner和她同坐。但是,每个女孩都有各自的想法,举个例子把,Rabbit只愿意和XHD或PQK做partner,Grass只愿意和linle或LL做partner,PrincessSnow愿意和水域浪子或伪酷儿做partner。考虑到经费问题,boss刘决定只让找到partner的人去坐过山车,其他的人,嘿嘿,就站在下面看着吧。聪明的Acmer,你可以帮忙算算最多有多少对组合可以坐上过山车吗?

输入数据的第一行是三个整数K , M , N,分别表示可能的组合数目,女生的人数,男生的人数。0<K<=1000,1<=N 和M<=500.接下来的K行,每行有两个数,分别表示女生Ai愿意和男生Bj做partner。最后一个0结束输入。

对于每组数据,输出一个整数,表示可以坐上过山车的最多组合数。

二、题目思路以及AC代码

说实话,这道题没什么好说的,只要是学过匈牙利算法,直接使用即可。

但这里,为了照顾一下没学过的同学,同时,也是为了让我自己复习一下今天所学,这里我就简单的梳理一下匈牙利算法的思路。

匈牙利算法

匈牙利算法是经典的用来求解二分图匹配问题的解法。

二分图

我在这里大致解释一下二分图,二分图的意思就是可以把图的所有顶点分解为两个集合S和T,图中的任何边的两个顶点只可能分别来自S和T,也就是说不可能在图中存在一条边,它的两个顶点全部来自集合S,或者全部来自集合T。更通俗的说,就是二分图的顶点可以完全划分为两类。下面给一个例子用来说明二分图。
在这里插入图片描述
如上图所示,就是一个二分图,可以看到图中黄色的顶点构成集合S,绿色的顶点构成集合T。黄色的顶点之间不存在连线,绿色的顶点之间也不存在连线,只有二者之间才有线,这就是二分图的特点。

二分图匹配

说完了二分图,下面我们来说一下匹配的问题。什么是匹配呢?我们在现实生活中也会遇到这样的问题,我举个例子就明白了。政府现在手头有K个工程任务需要完成,有N个公司可供政府调用,但不同的公司可以承担的任务种类有限,而且同一时间只能执行一个任务,如何找到一个好的分配方案,可以使完成的任务数最多呢?这就是一个典型的二分图匹配问题。

以上问题,K个工程任务就构成我的集合S,N个公司构成集合T,一个工程任务节点能且仅能连接一个公司节点,公司节点也只能连接一个工程节点。最终的目标是找寻一个连接方式,使图中边的数量尽量的多。

算法思想

针对上述的二分图问题,匈牙利科学家有一种解法,但其实思想也很朴素,可能我们生早一点也可以想的到。

我在这暂且大致说一下思想,具体算法可以参见我的另一篇转载博客,这里我就不重复说一遍了。匈牙利算法其实是做了一个什么事情呢?他就是利用回溯的思想,把所有的连接可能都遍历了一遍,你只要掌握了这个思想,你再去看匈牙利算法的解释,会更好理解一些,下面给我的另一篇博客链接。
【图论】匈牙利算法入门

hdu 2063代码

扯了半天,其实代码也挺简单的。

#include <iostream>
#define MAXN 505
using namespace std;

int K, M, N;
bool lines[MAXN][MAXN];			// lines[i][j]为true,表示i号男生可以和j号女生配对
bool used[MAXN];				// used[i]为true,表示在某一次的查找中,i号女生已经被预订了
int girl[MAXN];					// girl[i],表示i号已经归属于girl[i]号男生了

void init() {
	for (int i = 0; i < MAXN; i++) {
		for (int j = 0; j < MAXN; j++) {
			lines[i][j] = false;
		}
		used[i] = false;
		girl[i] = 0;
	}
}

bool find(int x) {
	for (int i = 1; i <= M; i++) {				// 遍历每一个妹子
		if (lines[x][i] && !used[i]) {			// 如果x和i号妹子可以匹配并且在此次搜索中i号没有被预定
			used[i] = true;						// 预定i号

			// 如果i号妹子还没有人选或者可以给当前i号妹子的人选找到另一个合适的
			if (girl[i] == 0 || find(girl[i])) { 
				girl[i] = x;
				return true;
			}
		}
	}
	return false;
}

int main()
{
	while (scanf("%d", &K)) {
		if (!K) break;
		scanf("%d%d", &M, &N);
		init();

		for (int i = 0; i < K; i++) {
			int a, b;
			scanf("%d%d", &a, &b);

			lines[b][a] = true;
		}

		int all = 0;
		for (int i = 1; i <= N; i++) {
			for (int j = 0; j < MAXN; j++) {
				used[j] = false;
			}
			if (find(i)) all++;
		}

		printf("%d\n", all);
	}

    return 0;
}

如有问题,欢迎大家指正!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值