【发布时间】:2021-10-26 22:42:00
【问题描述】:
我有一个相当大的二分图(每个部分约 200 个顶点,通常中间有 20,000 条或更多边),我试图在其中找到一个最小顶点覆盖,因为我正在寻找一个两个部分的顶点。
根据 Koenig 定理,存在与最大匹配 (https://en.wikipedia.org/wiki/K%C5%91nig%27s_theorem_(graph_theory)) 的基数相同大小的覆盖。
我已经实现了 Hopcroft Karp 算法,它给了我一个最大匹配。如果需要,我可以提供我的实现,但我怀疑这就是我的问题所在。
真正的问题是什么?
我怀疑我的实现,取自上面的维基百科文章 (https://en.wikipedia.org/wiki/K%C5%91nig%27s_theorem_(graph_theory)#Constructive_proof),其中有错误,但经过几个小时的检查后,我无法找到错误的原因:虽然 Hopcroft Karp 算法找到了最大匹配对于 192 个边,最小顶点覆盖返回 200 个顶点。由于这是一个二分图,因此这些数字不应该不同(因为定理)。也许你可以帮助我并告诉我我的错误在哪里。提前致谢!!
(Student's 和Project's 是我在二分图中的两种类型的顶点)
internal static List<Vertex> FindMinimumVertexCover(IReadOnlyList<Edge> matching, IReadOnlyList<Vertex> studentVertices, IReadOnlyList<Vertex> projectVertices)
{
var unmatchedStudentNodes = studentVertices.Except(matching.Select(e => e.GetStudentVertex())).ToList();
var visitedVertices = new List<Vertex>();
var edgeComparer = new EdgeComparer();
foreach (var unmatchedStudentNode in unmatchedStudentNodes)
{
visitedVertices = visitedVertices.Union(FindAlternatingNodes(matching, unmatchedStudentNode, visitedVertices, edgeComparer)).ToList();
}
visitedVertices = unmatchedStudentNodes.Union(visitedVertices).ToList();
return studentVertices.Except(visitedVertices).Union(projectVertices.Intersect(visitedVertices)).ToList();
}
private static List<Vertex> FindAlternatingNodes(IReadOnlyList<Edge> matching, Vertex initialVertex, List<Vertex> visitedVertices, EdgeComparer edgeComparer)
{
if (visitedVertices.Contains(initialVertex))
return Enumerable.Empty<Vertex>().ToList();
visitedVertices.Add(initialVertex);
List<Edge> unmatchedEdges = initialVertex.Edges.Except(matching, edgeComparer).ToList();
foreach (Edge unmatchedEdge in unmatchedEdges)
{
Vertex visitedVertex = unmatchedEdge.GetProjectVertex();
Edge matchedEdge = matching.SingleOrDefault(e => e.GetProjectVertex().Equals(visitedVertex));
if (matchedEdge != default(Edge))
{
visitedVertices.Add(visitedVertex);
visitedVertex = matchedEdge.GetStudentVertex();
visitedVertices = visitedVertices.Union(FindAlternatingNodes(matching, visitedVertex, visitedVertices, edgeComparer)).ToList();
}
}
return visitedVertices;
}
class EdgeComparer : IEqualityComparer<Edge>
{
public bool Equals(Edge x, Edge y)
{
if (Object.ReferenceEquals(x, y))
return true;
if (x is null || y is null)
return false;
return Object.ReferenceEquals(x.GetStudentVertex(), y.GetStudentVertex()) && Object.ReferenceEquals(x.GetProjectVertex(), y.GetProjectVertex());
}
public int GetHashCode(Edge edge)
{
return (Student: edge.GetStudentVertex(), Project: edge.GetProjectVertex()).GetHashCode();
}
}
【问题讨论】:
-
很容易验证匹配(不一定是最大值),很容易验证顶点覆盖(不一定是最小值),并且很容易验证它们是否具有相同的大小(因此两者都被 LP 优化双重性)。这意味着您可以对小型随机图进行全自动测试,我保证从经验中可以解决问题。然后,您可以选择一个重现错误的图表,手动解决它,并应用标准的调试技术。
-
似乎是一个有趣的问题,但需要minimal reproducible example。我怀疑你可以用更小的图表重现这个问题。假设您尝试的图形每个部分有 200 个顶点,那么我的猜测是该算法只是在一个部分中返回 all 的顶点。这将保证覆盖范围,因为所有边缘都在两个部分之间。
标签: c# algorithm graph-algorithm