二分图的完美匹配

二分图的完美匹配
今天学了KM算法,用于二分图的完美匹配,以前就遇到过很多次,但是一直都没有花时间去学
学的比较挫,写的是n^4的算法
实际上有m*m*n的算法的
下边几道是完美匹配的题目
http://info.zjfc.edu.cn/acm/problemDetail.aspx?pid=1222 赤裸裸的完美匹配,图都建好了
http://acm.hdu.edu.cn/showproblem.php?pid=1533 这个建图很容易
http://acm.hdu.edu.cn/showproblem.php?pid=2282 这个需要建图

下边是http://acm.hdu.edu.cn/showproblem.php?pid=1533 的AC代码
//////////////////////////////////////////////////////////////////////// //
// 二分图的完美匹配,Kuhn-Munkras算法
#include < stdio.h >
#include
< math.h >
#include
< string >
#define  MAXN 101
//////////////////////////////////////////////////////////////////////// //
#define  inf 0x7FFFFFFF
int  edge[MAXN][MAXN];
int  match[MAXN];
bool  hash[MAXN][MAXN];
bool  xhash[MAXN],yhash[MAXN];
char  map[MAXN][MAXN];
int  min( int  a, int  b){ return  a > b ? b:a;}
int  max( int  a, int  b){ return  a > b ? a:b;}
void  Scanf( int  n, int  m, int   & l);
bool  dfs( int  p, int  n) // 找增广路径
{
    
int  i;
    
for (i = 0 ;i < n;i ++ )
    {
        
if ( ! yhash[i]  &&  hash[p][i])
        {
            yhash[i] 
=   true ;
            
int  t  =  match[i];
            
if (t  ==   - 1   ||  dfs(t,n))
            {
                match[i] 
=  p;
                
return   true ;
            }
            
if (t  !=   - 1 )
                xhash[t] 
=   true ;
        }
    }
    
return   false ;
}
void  show( int  id, int  n)
{
    
int  i,j;
    
for (i = 0 ;i < n;i ++ )
    {
        
for (j = 0 ;j < n;j ++ )
        {
            
if (id)
                printf(
" %d  " ,edge[i][j]);
            
else
                printf(
" %d  " ,hash[i][j]);
        }
        puts(
"" );
    }
    puts(
"" );
}
void  KM_Perfect_Match( int  n)
{
    
int  i,j;
    
int  xl[MAXN],yl[MAXN];
    
for (i = 0 ;i < n;i ++ )
    {
        xl[i] 
=   0 ;
        yl[i] 
=   0 ;
        
for (j = 0 ;j < n;j ++ )
            xl[i] 
=  max(xl[i],edge[i][j]);
    }
    
bool  perfect  =   false ;
    
while ( ! perfect)
    {
        
for (i = 0 ;i < n;i ++ ) // 现阶段已经匹配的路
        {
            
for (j = 0 ;j < n;j ++ )
            {
                
if (xl[i]  +  yl[j]  ==  edge[i][j])
                    hash[i][j] 
=   true ;
                
else
                    hash[i][j] 
=   false ;
            }
        }

//         show(0,n);
         int  cnt  =   0 ;
        memset(match,
- 1 , sizeof (match));
        
for (i = 0 ;i < n;i ++ ) // 当前的图是否能全部匹配
        {
            memset(xhash,
false , sizeof (xhash));
            memset(yhash,
false , sizeof (yhash));
            
if (dfs(i,n))
                cnt 
++ ;
            
else
            {
                xhash[i] 
=   true ;
                
break ;
            }
        }
        
if (cnt  ==  n) // 没有增广路径
            perfect  =   true ;
        
else
        {
            
int  ex  =  inf;
            
for (i = 0 ;i < n;i ++ )
            {
                
for (j = 0 ;xhash[i]  &&  j < n;j ++ )
                {
                    
if ( ! yhash[j])
                        ex 
=  min(ex,xl[i]  +  yl[j]  -  edge[i][j]); // 找到一条没建的边的最小值
                }
            }
            
for (i = 0 ;i < n;i ++ ) // 根据这个边来进行松弛
            {
                
if (xhash[i])
                    xl[i] 
-=  ex;
                
if (yhash[i])
                    yl[i] 
+=  ex;
            }
        }
    }
}
int  main()
{
    
int  n,m,l;
    
while (scanf( " %d%d " , & n, & m))
    {
        
if (n  ==   0   &&  m  ==   0 )
            
break ;
        Scanf(n,m,l);
        KM_Perfect_Match(l);
        
int  mindis  =   0 ;
        
for ( int  i = 0 ;i < l;i ++ )
            mindis 
+=  edge[match[i]][i];
        printf(
" %d\n " , - mindis);
    }
    
return   0 ;
}

// 读入建图
void  Scanf( int  n, int  m, int   & l)
{
    
int  i,j,l1,l2;
    
struct  Point{
        
int  x,y;
    }MAN[MAXN],HOUSE[MAXN];
    l1 
=  l2  =   0 ;
    
for (i = 0 ;i < n;i ++ )
    {
        scanf(
" %s " ,map[i]);
        
for (j = 0 ;j < m;j ++ )
        {
            
if (map[i][j] == ' m ' )
            {
                MAN[l1].x 
=  i;
                MAN[l1].y 
=  j;
                l1 
++ ;
            }
            
else   if (map[i][j] == ' H ' )
            {
                HOUSE[l2].x 
=  i;
                HOUSE[l2].y 
=  j;
                l2 
++ ;
            }
        }
    }
    l 
=  l1;
    
for (i = 0 ;i < l;i ++ )
        
for (j = 0 ;j < l;j ++ )
            edge[i][j] 
=   - abs(MAN[i].x  -  HOUSE[j].x)  -  abs(MAN[i].y  -  HOUSE[j].y);
}

你可能感兴趣的:(二分图的完美匹配)