问题定义及参考
给定一个简单的图(无向、无自边、无多边)匹配
是边的子集,使得它们中没有两条与同一个顶点相关。
完美匹配是所有顶点都与
匹配,如果有奇数个顶点,这是不可能的。
更一般地,我们可以要求最大匹配(最大可能的数量
匹配中的边)或最大匹配(不再匹配的匹配
可以添加边)。
如果将正实“权重”分配给边缘,我们可以概括
最大加权匹配的问题,一个最大化
边权重的总和。精确的最大加权匹配问题可以是
在 O(nm log(n)) 时间内求解,其中 n 是顶点数,m 是
边数。
请注意,最大加权匹配不一定是完美匹配。为了
示例:
*--1--*--3--*--1--*
只有一个完美匹配,其总权重为 2,最大
权重为 3 的加权匹配。
关于精确和近似解的讨论和进一步参考
这些,以及最小加权完美匹配问题,可以找到
在这些论文中:
"A Simple Approximation Algorithm for the Weighted Matching Problem"
Drake, Doratha E. 和 Hougardy, Stefan (2002)
Implementation of O(nm log n) Weighted Matchings The Power of Data Structures
Melhorn、Kurt 和 Schäfer、Guido (2000)
Computing Minimum-Weight Perfect Matchings
Cook, William 和 Rohe, André (1997)
Approximating Maximum Weight Matching in Near-linear Time
Duan, Ran 和 Pettie, Seth (2010)
Drake 和 Hougardy 的简单逼近算法
Drake-Hougardy 的第一个近似算法使用了这个思想
使用在每个顶点相遇的局部最重边的增长路径。它
与贪心算法一样,“性能比”为 1/2,但线性
边数的时间复杂度(贪心算法使用
全局最重的边缘,并且会导致更大的时间复杂度来找到它)。
主要的实现任务是识别支持的数据结构
高效地执行他们的算法的步骤。
PathGrowing算法的思路:
Given: a simple undirected graph G with weighted edges
(0) Define two sets of edges L and R, initially empty.
(1) While the set of edges of G is not empty, do:
(2) Choose arbitrary vertex v to which an edge is incident.
(3) While v has incident edges, do:
(4) Choose heaviest edge {u,v} incident to v.
(5) Add edge {u,v} to L or R in alternating fashion.
(6) Remove vertex v (and its incident edges) from G.
(7) Let u take the role of v.
(8) Repeat 3.
(9) Repeat 1.
Return L or R, whichever has the greater total weight.
表示图形和输出的数据结构
由于“集合”在任何直接意义上都不是 C 的数据结构,所以我们
需要决定边和顶点的容器类型
适合这个算法。关键操作是删除顶点
和入射边,使我们能够找到是否有边
left 并比较与 a 相关的剩余边的权重
给定顶点。
边缘需要是可搜索的,但只能查看是否还剩下任何边缘。
人们首先想到的是一个简单的边链表,没有任何特殊
订购。但是这个列表也需要通过本质上来维护
随机删除。这表明双向链表(反向链接为
以及在每个节点处转发),以便可以删除一条边
通过修复链接以跳过任何“已删除”节点。边权重
也可以存储在相同的结构中。
此外,我们需要能够扫描所有(剩余)边缘事件
给定的顶点。我们可以通过为每个顶点创建一个链表来做到这一点
的(指向)入射边。我会假设顶点有
已预处理为可用作索引的序数值
指向这些链表的指针数组。
最后我们需要表示边集 L 和 R,其中之一是
作为近似最大匹配返回。我们的要求
能够将边添加到任一集合,并且能够总计
两者的边权重。动态分配的链表
节点可以达到这个目的,也许存储指向边缘节点的指针
在原来的双向链表中,权重属性仍然会
即使在边缘被链接操作“移除”后仍然存在。
这样的链表和双向链表可以按时间比例创建
到边的数量,因为双向链表条目可能是
分配给输入上的特定于顶点的链接。有了这样的设计
请注意,我们可以分析算法的每个步骤所需的工作量。
(待续)