这段时间要沉迷刷题一段时间了,就让优快云陪我一起吧!
一、题目大意
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;
}
如有问题,欢迎大家指正!!!