我该如何计算最小二分图顶点覆盖?

3
我该如何在C#中计算最小的二分图顶点覆盖?是否有相关代码片段可供使用?
编辑:尽管对于一般图,该问题是NP完全的,但对于二分图,它可以在多项式时间内解决。我知道这与最大匹配有关,通过Konig定理可以在二分图中实现,但我无法正确理解该定理以便将最大二分匹配结果转换为顶点覆盖。
3个回答

5
我能够理解它的含义:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

class VertexCover
{
    static void Main(string[] args)
    {
        var v = new VertexCover();
        v.ParseInput();
        v.FindVertexCover();
        v.PrintResults();
    }

    private void PrintResults()
    {
        Console.WriteLine(String.Join(" ", VertexCoverResult.Select(x => x.ToString()).ToArray()));
    }

    private void FindVertexCover()
    {
        FindBipartiteMatching();

        var TreeSet = new HashSet<int>();
        foreach (var v in LeftVertices)
            if (Matching[v] < 0)
                DepthFirstSearch(TreeSet, v, false);

        VertexCoverResult = new HashSet<int>(LeftVertices.Except(TreeSet).Union(RightVertices.Intersect(TreeSet)));
    }

    private void DepthFirstSearch(HashSet<int> TreeSet, int v, bool left)
    {
        if (TreeSet.Contains(v))
            return;
        TreeSet.Add(v);
        if (left) {
            foreach (var u in Edges[v])
                if (u != Matching[v])
                    DepthFirstSearch(TreeSet, u, true);
        } else if (Matching[v] >= 0)
            DepthFirstSearch(TreeSet, Matching[v], false);

    }

    private void FindBipartiteMatching()
    {
        Bicolorate();
        Matching = Enumerable.Repeat(-1, VertexCount).ToArray();
        var cnt = 0;
        foreach (var i in LeftVertices) {
            var seen = new bool[VertexCount];
            if (BipartiteMatchingInternal(seen, i)) cnt++;
        }
    }

    private bool BipartiteMatchingInternal(bool[] seen, int u)
    {
        foreach (var v in Edges[u]) {
            if (seen[v]) continue;
            seen[v] = true;
            if (Matching[v] < 0 || BipartiteMatchingInternal(seen, Matching[v])) {
                Matching[u] = v;
                Matching[v] = u;
                return true;
            }
        }
        return false;
    }

    private void Bicolorate()
    {
        LeftVertices = new HashSet<int>();
        RightVertices = new HashSet<int>();

        var colors = new int[VertexCount];
        for (int i = 0; i < VertexCount; ++i)
            if (colors[i] == 0 && !BicolorateInternal(colors, i, 1))
                throw new InvalidOperationException("Graph is NOT bipartite.");
    }

    private bool BicolorateInternal(int[] colors, int i, int color)
    {
        if (colors[i] == 0) {
            if (color == 1) LeftVertices.Add(i);
            else RightVertices.Add(i);
            colors[i] = color;
        } else if (colors[i] != color)
            return false;
        else
            return true;
        foreach (var j in Edges[i])
            if (!BicolorateInternal(colors, j, 3 - color))
                return false;
        return true;
    }

    private int VertexCount;
    private HashSet<int>[] Edges;
    private HashSet<int> LeftVertices;
    private HashSet<int> RightVertices;
    private HashSet<int> VertexCoverResult;
    private int[] Matching;

    private void ReadIntegerPair(out int x, out int y)
    {
        var input = Console.ReadLine();
        var splitted = input.Split(new char[] { ' ' }, 2);
        x = int.Parse(splitted[0]);
        y = int.Parse(splitted[1]);
    }

    private void ParseInput()
    {
        int EdgeCount;
        ReadIntegerPair(out VertexCount, out EdgeCount);
        Edges = new HashSet<int>[VertexCount];
        for (int i = 0; i < Edges.Length; ++i)
            Edges[i] = new HashSet<int>();

        for (int i = 0; i < EdgeCount; i++) {
            int x, y;
            ReadIntegerPair(out x, out y);
            Edges[x].Add(y);
            Edges[y].Add(x);
        }
    }
}

正如您所看到的,这段代码可以在多项式时间内解决问题。


对于其他使用此解决方案的人来说,它非常接近,但其中存在一个根本性错误。在DepthFirstSearch开始时传递了false,而应该是true。 - spotter

1

最好随机选择一个节点。对于每个节点,要么它本身在顶点覆盖中,要么它的所有邻居都在其中(因为需要包括该边)。整个过程的最终结果将是一组顶点覆盖,然后选择最小的那个。不过,我不会在这里编写代码,因为如果我没记错的话,这是NP完全问题。


最小顶点覆盖应该是NP完全问题。二分图可能只是图的一个属性。SteveR应该在某处咨询NP完全问题的近似解决方案... - Calyth
我想要双分图版本。我知道它与匹配有关(Konig定理)。但我不知道如何将双分图匹配的结果转换为最小顶点覆盖。 - SteveR
我的错误,我没有看到那里的“二分图”这个词。 - GWLlosa

0
私有的void DepthFirstSearch(HashSet TreeSet, int v, bool left) { if (TreeSet.Contains(v)) return; TreeSet.Add(v); if (left) { foreach (var u in Edges[v]) if (u != Matching[v]) DepthFirstSearch(TreeSet, u, true); } else if (Matching[v] >= 0) DepthFirstSearch(TreeSet, Matching[v], false);
}
当if(left)执行时,我认为它的值总是false。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接