大意:
奶牛对牛栏有各自的喜好,一个奶牛进一个牛栏,一个牛栏只能放一只奶牛,问最多能放多少奶牛。
分析与实现:
最典型的二分图最大匹配问题。毫无疑问,用匈牙利算法。算法本身很简单:对每个节点,找增广轨。
对每个牛节点做深度优先搜索的过程可以用直观的方式来描述,一头奶牛找牛栏的过程如下:
奶牛观察自己所喜欢的所有牛栏
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;
}