杭电 2063 过山车

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

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

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

Sample Input
 6 3 3
1 1
1 2
1 3
2 1
2 3
3 1
0

Sample Output
 3

分析:


数据结构:
保存二分图用的是邻接表。
为方便存储和阅读,数组下标从1开始(0那个空间就空着不用)。
因此,第i个表头邻接的是第i个女生愿意与他做partner的男生号。

集合X、Y用一维数组表示。下标同样从1开始。
如果它们暂时没有点配对,则里面保存的是O,否则保存配对的点的下标。
初始状态X、Y都为0。

因为X是从1到n循环,所以只需要判断Y集合中的点是否被访问过。
判断是否被访问过用一个Bool型一维数组,true表示被访问过,false表示未被访问过。

中间记录增广路径用栈结构。因为增广路径的边数是奇数,根据握手定理,它的顶点数一定是偶数。
找到增广路径以后,每次从栈顶取出2个端点,就是现在相互配对的端点了。一直到栈取空为止。

算法实现:
实现匈牙利算法分为4个步骤:

  1. 初始化:
    集合X、Y清空
    图清空

  2. 输入:
    输入集合X、Y的大小size_x和size_y,以及所有的边

  3. 计算:
    i从1循环到size_x,深搜寻找Xi的增广路径
    每次寻找前要先清空栈,并且初始Visit数组为false
    在深搜的过程中,要注意,如果寻找增广路径失败,要记得把刚加入的那一个顶点从栈中删除
    如果增广路径查找成功,就开始读栈,对X、Y里相互配对的端点进行标记,直到栈为空

  4. 输出:
    统计X(Y)集合中有配对的端点个数,即最大匹配数。
    输出最大匹配数 


代码实现:


#include <stack>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;

typedef struct node
{
	int ord;
	node * next;
}Node;

const int size = 512 + 1;

Node	list[size];
int	X[size];
int	Y[size];
bool	visit[size];
int	size_x;
int	size_y;
stack	<int>	stk;

void init()
{
	int	i;

	for (i = 1; i <= size_x; i++)
		list[i].next = NULL;
	memset(X, 0, (size_x + 1) * sizeof(int));
	memset(Y, 0, (size_y + 1) * sizeof(int));
}

void input(int len)
{
	int	i;
	int	x;
	int	y;
	Node*	p;

	for (i = 0; i < len; i++)
	{
		scanf("%d%d", &x, &y);
		p = new Node;
		p->ord = y;
		p->next = list[x].next;
		list[x].next = p;
	}
}

bool dfs(int x)
{
	Node*	p;

	stk.push(x);
	for (p = list[x].next; p; p = p->next)
	{
		if (!visit[p->ord])
		{
			visit[p->ord] = true;
			stk.push(p->ord);
			if (!Y[p->ord] || dfs(Y[p->ord]))
				return true;
			else
				stk.pop();
		}
	}
	stk.pop();

	return false;
}

void solve()
{
	int	i;
	int	top;

	for (i = 1; i <= size_x; i++)
	{
		while (!stk.empty()) stk.pop();
		memset(visit, false, size_y + 1);
		if (dfs(i))
		{
			while (!stk.empty())
			{
				top = stk.top();
				stk.pop();
				Y[top] = stk.top();
				X[stk.top()] = top;
				stk.pop();
			}
		}
	}
}

void output()
{
	int	i;
	int	count = 0;
	Node*	p;

	for (i = 1; i <= size_x; i++)
	{
		if (X[i]) count++;
		while (p = list[i].next)
		{
			list[i].next = p->next;
			delete p;
		}
	}

	cout << count << endl;

}

int main(void)
{
	int	n;

	while (scanf("%d", &n), n)
	{
		scanf("%d%d", &size_x, &size_y);

		init();
		input(n);
		solve();
		output();

		while (!stk.empty()) stk.pop();
	}

	return 0;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值