二分匹配

图采用矩阵存储,下标从1开始。

匈牙利匹配算法的理论和证明不复杂,就是不断寻找两个端点都是未浸润点的交替路径,把路径上的边匹配状态全部取反。每次操作,图的匹配数都增加1。为方便描述,将二部图划分为X和Y两个部集。
此算法有几个性质:
1.算法只需以某一个部集(可以是X或Y)中的所有未浸润点为交替路径的起始点,展开寻找。
2.X中的某个点,只有在以它为起始点进行过交替路径搜索后,才有可能变为浸润点。
3.以X中的某个点为起始点,如果无法找到交替路径,那么以后不论何时以它为起始点,都不可能找到交替路径。
4.据1,选择处理X集,由2,3知X集中的所有点最多只需处理一次。

DFS:
 1  int  w[NX][NY];  // X中的点到Y中的点的连接矩阵,w[i][j]是边<Vxi,Vyj>的权
 2  int  m[NY];  // Vyi的匹配对象
 3  int  v[NY];  // 在一次DFS中,Vyi是否被访问过
 4  bool  dfs( int  k){  // 以Vxk为起点找交替路径
 5       int  i;
 6       for (i = 1 ;i <= N;i ++ ){
 7           if ( ! v[i]  &&  w[k][i]){  // 如果Vyi未访问过,而且Vxk,Vyi有边连接
 8              v[i] = 1 ;
 9               if ( ! m[i]  ||  dfs(m[i])){  // 如果Vyi是未浸润点,或者以Vyi原来的匹配点为起始,有扩张路径
10                  m[i] = k;
11                   return   true // 扩张成功
12              }
13          }
14      }
15       return   false ;
16  }

这个算法也可以从类似“贪心”的角度理解:一个X中的点Vx0,如果找到某个能到达的Y点Vy0,就暂时把它“据为己有”,然后看Y的“原配X点”还能不能找到别的Y点配对,如果能,那么Vx0和Vy0就配对成功,否则不成功,Vx0继续寻找别的Vy。

BFS:
这是我在某些算法书上看到的BFS版本,需要用变量(或变量数组)tag记录扩展目标。代码中,我改为用que[i]的bit1记录:
 1  int  w[NX],ma[NY];
 2  int  que[NX + NY],pq,sq;  // 广搜队列
 3  int  pax[NX],pay[NY];  // 记录交替路径
 4  bool  bfs( int  r){
 5       int  i,j,k,tag;  // tag,记录交替路径的下一步要扩展X中的点(tag==1时),还是Y中的点(tag==0时)
 6      memset(pax, 0 , sizeof (pax));
 7      memset(pay, 0 , sizeof (pay));
 8      que[ 0 ] = (r << 1 );
 9      pq = 0 sq = 1 ;
10       while (pq < sq){
11          k = que[pq] >> 1 tag = que[pq] & 1 ;
12           if (tag == 0 ){  // process set X, look for unvisited vex in Y
13               for (i = 1 ;i <= N;i ++ ){
14                   if ( ! pay[i]  &&  w[k][i]){
15                      pay[i] = k;
16                       if ( ! ma[i]){  // 是未浸润点,扩展路径搜索完毕
17                           for (j = i;j;j = pax[pay[j]]){
18                              ma[j] = pay[j];
19                          }
20                           return   true ;
21                      }
22                       else // 这个Y点不是未浸润点,入队
23                          que[sq ++ ] = (i << 1 ) | 1 ;
24                      }
25                  }
26              }
27          }
28           else // Vyk不是未浸润点,路径必须沿着它所在的匹配边扩展
29              que[sq ++ ] = (ma[k] << 1 );
30              pax[ma[k]] = k;
31          }
32          pq ++ ;
33      }
34       return   false ;
35  }


其实,在遇到浸润的Vyi时,由于下一步只能沿着它的匹配点Vxj走,即排队轮到Vyi时,必定是Vxj被加入队列。因此,只要令队列que仅存放X集的点,每次遇到浸润的Vyi,把它的匹配点Vxj加入队列。这样就省去了分支,减小了代码量。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值