ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

最大权完美匹配:KM算法的优化

2020-03-24 10:01:31  阅读:263  来源: 互联网

标签:结点 相等 匹配 子图 KM 算法 ij 交错


我们知道最大权完美匹配的KM算法。简述其流程如下:

  • 设二分图的两部分点集分别为 $X=\{X_1, X_2, \ldots, X_n\}$ 和 $Y=\{Y_1, Y_2, \ldots, Y_m\}$, $X_iY_j$ 间边权为 $w_{ij}$.
  • 给两部点集分别赋点权 $\{A_i\}, \{B_i\}$, 使得 $A_i+B_j \ge w_{ij}$. 取等的边的生成子图叫做相等子图。那么相等子图的完美匹配就是最大权匹配。
  • 我们需要适当选取权值,使相等子图有完美匹配。一开始令 $X=\emptyset$, $B_i=0$, 并逐个加入 $X_1, X_2, \ldots, X_n$.
  • 假设目前需要加入 $X_i$, 由定义可取 $A_i=\max\{w_{ij}\}$. 为了在相等子图上找到完美匹配,需要找到一条从 $X$ 到 $Y$ 的交错路。如果交错路存在,算法结束。
  • 如果交错路不存在,取出在搜索过程中访问的所有点,它们构成交错树,记其顶点集与 $X$ 的交为 $X'$, 与 $Y$ 的交为 $Y'$. 对于所有和 $X'$ 中的点相邻的非匹配边 $\left<X_i, Y_j\right>$,设 $A_i+B_j-w_{ij}$ 的最小值为 $d$. 将 $X'$ 中的所有点权减小 $d$, $Y'$ 中的所有点权增大 $d$, 由此可知 $A_i+B_j \ge w_{ij}$ 仍然满足。交错树上的边仍然属于相等子图,且至少有一条与交错树相邻的相等边,所以可在此基础上继续找交错路。

KM算法需要对每回修改后的子图重新搜索交错路,时间复杂度可达 $O(n^4)$.

由于原交错树仍然是可用的,我们考虑不重新搜索交错路,而是在原交错树上直接扩展新边。

具体地,我们维护交错树,对于每个 $Y'$ 中的点,记录它的父结点。对于 $Y_j \in Y \setminus Y'$, 我们维护 $slack_j=\min\{A_i+B_j-w_{ij}\ \mid X_i \in X'\}$, 取得最小值的 $X_i$ 是它的准父结点(有多个任取一个)。

那么,由交错树仅单点 $X_k$ 开始,取出 $d=\min\{slack_j \mid Y_j \in Y \setminus Y'\}$, 作上述第五步的更新,然后就可以将取得最小值的 $j$(有多个任取一个)加入交错树,$Y_j$ 的父结点就是原准父结点。特别地,当 $d=0$ 是就是继续沿原相等子图扩展。若所加入的 $Y_j$ 还未匹配,则说明已经找到交错路,顺着父结点构成的路径一路修改匹配即可。在实现上,我们记 $match_j$ 表示 $Y_j$ 的匹配点,$pre_j$ 表示 $Y_j$ 的父结点的匹配点,不存在记为 $0$.

代码

以下假设 $n \le 500$, 答案绝对值小于 $10^{18}$, 输入点数和边权,输出 $Y_j$ 的匹配点。

 1 #include <bits/stdc++.h>
 2 const int N=501;
 3 int n, match[N];
 4 bool vis[N];
 5 long long f[N][N], a[N], b[N], slack[N], pre[N];
 6 template<class T1, class T2> bool cmin(T1 &a, const T2 &b)
 7 {
 8     return b<a?(a=b, true):false;
 9 }
10 template<class T1, class T2> bool cmax(T1 &a, const T2 &b)
11 {
12     return a<b?(a=b, true):false;
13 }
14 int main()
15 {
16     scanf("%d", &n);
17     for(int i=1; i<=n; ++i) {
18         for(int j=1; j<=n; ++j)
19             scanf("%d", f[i]+j);
20         a[i]=*std::max_element(f[i]+1, f[i]+n+1);
21     }
22     for(int i=1; i<=n; ++i) {
23         int x=0, cho;
24         memset(vis+1, 0, n);
25         memset(pre+1, 0, n*sizeof(int));
26         memset(slack+1, 63, n*sizeof(long long));
27         match[0]=i;
28         do {
29             int u=match[x];
30             long long min=1e18;
31             vis[x]=true;
32             for(int v=1; v<=n; ++v) {
33                 if(!vis[v]) {
34                     long long t=a[u]+b[v]-f[u][v];
35                     if(cmin(slack[v], t))
36                         pre[v]=x;
37                     if(cmin(min, slack[v]))
38                         cho=v;
39                 }
40             }
41             for(int j=0; j<=n; ++j) {
42                 if(vis[j]) {
43                     a[match[j]]-=min;
44                     b[j]+=min;
45                 } else
46                     slack[j]-=min;
47             }
48             x=cho;
49         } while(match[x]);
50         while(x) {
51             match[x]=match[pre[x]];
52             x=pre[x];
53         }
54     }
55     for(int i=1; i<=n; ++i)
56         printf("%d%c", match[i], " \n"[i==n]);
57     return 0;
58 }

标签:结点,相等,匹配,子图,KM,算法,ij,交错
来源: https://www.cnblogs.com/nealchen/p/improved-KM.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有