二分图最小点覆盖:从概念到算法实现

1. 引言

二分图最小点覆盖是图论中的一个经典问题,在任务调度、资源分配、网络流建模等场景中有着广泛应用。本文将从基本概念出发,介绍最小点覆盖与最大匹配之间的等价关系,并给出完整的算法实现与代码示例。

2. 基本概念

在正式讨论算法之前,先明确几个核心定义。

  • 二分图:顶点集可以划分为两个互不相交的子集 U 和 V,使得每条边的两个端点分别属于 U 和 V。
  • 点覆盖:给定图 G = (V, E),若存在一个顶点集合 S ⊆ V,使得图中每条边至少有一个端点属于 S,则称 S 为图 G 的一个点覆盖。
  • 最小点覆盖:在所有点覆盖中,包含顶点数量最少的点覆盖称为最小点覆盖。

对于一般图,求最小点覆盖是 NP 完全问题;但对于二分图,存在多项式时间的高效解法,其理论基础是柯尼希定理。

3. 柯尼希定理

柯尼希定理(König's Theorem)是二分图理论中最重要的结论之一,它建立了最小点覆盖与最大匹配之间的桥梁。

定理内容:在二分图中,最小点覆盖的大小等于最大匹配的大小。

这一定理的意义在于:求二分图的最小点覆盖,可以转化为求二分图的最大匹配,而后者可以通过匈牙利算法在多项式时间内高效求解。

4. 算法思路

基于柯尼希定理,求解二分图最小点覆盖的算法分为两步:

  1. 求最大匹配:使用匈牙利算法求出二分图的最大匹配。
  2. 构造最小点覆盖:从最大匹配出发,通过交替路径构造出最小点覆盖集合。

构造最小点覆盖的具体步骤如下:

  • 从左侧所有未匹配顶点出发,沿交替路径遍历:先走非匹配边,再走匹配边,交替进行。
  • 标记所有被访问到的顶点。
  • 最小点覆盖 = 左侧未访问的顶点 ∪ 右侧已访问的顶点。

5. 代码实现

下面给出一个完整的 Java 实现,包含匈牙利算法求最大匹配,以及从匹配结果构造最小点覆盖的过程。

import java.util.*;

public class MinVertexCover {
    private int n, m;          // 左侧顶点数 n,右侧顶点数 m
    private List<Integer>[] adj; // 邻接表
    private int[] matchR;      // 右侧顶点匹配的左侧顶点
    private boolean[] visited; // 右侧顶点是否被访问

    public MinVertexCover(int n, int m) {
        this.n = n;
        this.m = m;
        adj = new List[n];
        for (int i = 0; i < n; i++) {
            adj[i] = new ArrayList<>();
        }
        matchR = new int[m];
        Arrays.fill(matchR, -1);
    }

    public void addEdge(int u, int v) {
        adj[u].add(v);
    }

    // 匈牙利算法:尝试为左侧顶点 u 寻找匹配
    private boolean dfs(int u, boolean[] seen) {
        for (int v : adj[u]) {
            if (seen[v]) continue;
            seen[v] = true;
            if (matchR[v] == -1 || dfs(matchR[v], seen)) {
                matchR[v] = u;
                return true;
            }
        }
        return false;
    }

    // 求最大匹配
    public int maxMatching() {
        int result = 0;
        for (int u = 0; u < n; u++) {
            boolean[] seen = new boolean[m];
            if (dfs(u, seen)) {
                result++;
            }
        }
        return result;
    }

    // 构造最小点覆盖
    public List<Integer> minVertexCover() {
        maxMatching();
        int[] matchL = new int[n];
        Arrays.fill(matchL, -1);
        for (int v = 0; v < m; v++) {
            if (matchR[v] != -1) {
                matchL[matchR[v]] = v;
            }
        }

        // 从左侧未匹配顶点出发,沿交替路径遍历
        boolean[] visitedL = new boolean[n];
        boolean[] visitedR = new boolean[m];
        Queue<Integer> queue = new LinkedList<>();

        for (int u = 0; u < n; u++) {
            if (matchL[u] == -1) {
                queue.offer(u);
                visitedL[u] = true;
            }
        }

        while (!queue.isEmpty()) {
            int u = queue.poll();
            for (int v : adj[u]) {
                if (!visitedR[v]) {
                    visitedR[v] = true;
                    if (matchR[v] != -1 && !visitedL[matchR[v]]) {
                        visitedL[matchR[v]] = true;
                        queue.offer(matchR[v]);
                    }
                }
            }
        }

        // 最小点覆盖 = 左侧未访问 ∪ 右侧已访问
        List<Integer> cover = new ArrayList<>();
        for (int u = 0; u < n; u++) {
            if (!visitedL[u]) cover.add(u);
        }
        for (int v = 0; v < m; v++) {
            if (visitedR[v]) cover.add(n + v);
        }
        return cover;
    }

    public static void main(String[] args) {
        // 示例:左侧 3 个顶点,右侧 3 个顶点
        MinVertexCover solver = new MinVertexCover(3, 3);
        solver.addEdge(0, 0);
        solver.addEdge(0, 1);
        solver.addEdge(1, 1);
        solver.addEdge(2, 1);
        solver.addEdge(2, 2);

        int maxMatch = solver.maxMatching();
        System.out.println("最大匹配数: " + maxMatch);

        List<Integer> cover = solver.minVertexCover();
        System.out.println("最小点覆盖大小: " + cover.size());
        System.out.println("最小点覆盖顶点: " + cover);
    }
}

6. 复杂度分析

匈牙利算法的时间复杂度为 O(n × e),其中 n 为左侧顶点数,e 为边数。构造最小点覆盖的 BFS 遍历同样为 O(n + e)。因此,整个算法的时间复杂度为 O(n × e),空间复杂度为 O(n + m + e)。

7. 应用场景

二分图最小点覆盖在实际问题中有着广泛的应用:

  • 任务调度:在机器与任务的二分图中,最小点覆盖对应最少的机器或任务集合,使得所有依赖关系都被覆盖。
  • 资源分配:在资源与需求的二分图中,最小点覆盖可用于确定最少的资源投入。
  • 网络流建模:最小点覆盖与最大流、最小割问题密切相关,常用于网络规划与优化。
  • 矩阵覆盖问题:在 0-1 矩阵中,用最少的行或列覆盖所有 1 的位置,可建模为二分图最小点覆盖。

8. 总结

二分图最小点覆盖是图论中一个优雅而实用的问题。借助柯尼希定理,我们将它转化为最大匹配问题,并通过匈牙利算法高效求解。理解这一转化过程,不仅有助于掌握二分图的核心性质,也能为解决实际工程中的组合优化问题提供有力工具。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值