USACO Training Section 4.2 The Perfect Stall 匈牙利算法的递归和非递归实现

英文原题 中文题译

 

大意:

 

奶牛对牛栏有各自的喜好,一个奶牛进一个牛栏,一个牛栏只能放一只奶牛,问最多能放多少奶牛。

 

分析与实现:

 

最典型的二分图最大匹配问题。毫无疑问,用匈牙利算法。算法本身很简单:对每个节点,找增广轨。

 

对每个牛节点做深度优先搜索的过程可以用直观的方式来描述,一头奶牛找牛栏的过程如下:

 

奶牛观察自己所喜欢的所有牛栏

    a) 如果这个牛栏已经换过一次牛了,看下一个

    b) 找到一个“新”的牛栏(没换过牛,或者是空的),打上标记。如果这个牛栏没被占用,则搜索成功。

    c) 否则,尝试把占用这个牛栏的奶牛赶走,让它去找新的牛栏(递归调用),如果该奶牛报告能找到新地方,则本次搜索成功,确定本奶牛占用本牛栏,并向上一头奶牛报告。

 

每个奶牛开始找牛栏之前清理上一次留下的标记,重复这个过程,最后请点一下进入牛栏的奶牛数量,就是所得的匹配。

 

网上流传的代码是递归实现的,不过大多数都是抄过来直接用,以至于没有注意到明显的问题:注意代码22行,几乎所有网上的代码都有这一行代码,其实是错的。这些算法没出问题的原因,是它们将二分图的两部分存放在一个邻接表中,这样,牛的标号和牛栏的标号是不一样的,这一行废代码也就不会带来副作用。不过可见“一大抄”。

 

重写了递归的代码,并重写了一份非递归实现的代码。非递归实现事实上是一个回溯搜索的过程,不是普通的深度优先搜索,这需要特别注意。

 

/*
ID: blackco3
TASK: stall4
LANG: C++
*/
#include <iostream>
#include <memory.h>
using namespace std;

const int _max_group_size_(200) ;
struct t_node {
	int n_adj;
	t_node *adjs[_max_group_size_] ;
} cows[_max_group_size_] , stalls[_max_group_size_] ;
int n_cow, n_stall ;
t_node *cow_in_stall[_max_group_size_] ;

int vis[_max_group_size_] ;
#ifdef __recursive__
int find_enpath( t_node *p_cow )
{
	//vis[ p_cow-cows ]=1 ;
	for( t_node **pp_stall = p_cow->adjs ; pp_stall != p_cow->adjs + p_cow->n_adj ; pp_stall++ ){
		register int stall_no = *pp_stall-stalls ;
		if( vis[stall_no] )
			continue ;
		vis[stall_no] = 1 ;
		if( !cow_in_stall[ stall_no ] || find_enpath( cow_in_stall[ stall_no ] ) ) {
			cow_in_stall[stall_no] = p_cow ;
			return 1 ;
		}
	}
	return 0 ;
}
#else
int stack[_max_group_size_], stall[_max_group_size_] ;
int find_enpath( t_node *p_cow )
{
	int top=0 ;
	stack[top]=p_cow-cows, stall[top]=0 ;
	do {
		register int cur_cow = stack[top] ;
		for( ; stall[top] != cows[cur_cow].n_adj ; stall[top]++){
			register int stall_no = cows[cur_cow].adjs[ stall[top] ]-stalls ;
			if( vis[ stall_no ] ) 
				continue ;
			vis[ stall_no ] = 1 ;
			if( !cow_in_stall[ stall_no ] ) {				
				while( top >=0 ) {
					stall_no = cows[ stack[top] ].adjs[ stall[top] ]-stalls ;
					cow_in_stall[stall_no] = cows + stack[top] ;
					top-- ;
				}
				return 1 ;
			}
			stack[++top] = cow_in_stall[ stall_no ]-cows, stall[top]=0 ;
			break ;
		} 
		while( top>=0 && cows[stack[top]].n_adj==stall[top] )
			top-- ;
	}while( top>=0 ) ;
	return 0 ;	
}
#endif

int max_match_Hungarian( ) {
	int n_match=0 ;
	for( t_node *pc=cows; pc != cows+n_cow; pc++ ){
		memset( vis, 0, sizeof(int)*n_stall );
		n_match += find_enpath( pc ) ;
	}
	return n_match ;
}

int main() {
    freopen("stall4.in", "r", stdin); 
    freopen("stall4.out","w",stdout);

	cin >> n_cow >> n_stall ;
	for( t_node *pc=cows; pc != cows+n_cow ; pc++ ){
		cin >> pc->n_adj ;
		for( t_node **p_adj=pc->adjs ; p_adj!=pc->adjs+pc->n_adj ; p_adj++ ) {
			int stall_no ;
			cin >> stall_no ;
			*p_adj = stalls + (--stall_no);
			stalls[ stall_no ].adjs[ stalls[ stall_no ].n_adj++ ] = pc ;
		}
	}

	cout << max_match_Hungarian() << endl ;
	return 0; 
} 

 

你可能感兴趣的:(算法)