📜  覆盖二维网格的所有块所需的最小点数(1)

📅  最后修改于: 2023-12-03 15:41:33.707000             🧑  作者: Mango

覆盖二维网格的所有块所需的最小点数

问题概述

给定一个二维网格,求覆盖该网格的所有块所需的最小点数。

一个点可以覆盖网格中的一行或一列,或与一行或一列相交的格子。

问题分析

该问题可以转化为一个二分图的最小顶点覆盖问题。

将网格中的每行和每列看作一个顶点,如果一个点覆盖了某一行或某一列,则在对应的顶点之间连一条边。

则覆盖所有块所需的最小点数等价于在该二分图中选出尽量少的顶点,使得每条边都至少有一个端点被选中。

最小顶点覆盖问题可以通过求最大匹配问题来解决。

解法
  • 使用 Hopcroft-Karp 算法求二分图的最大匹配
  • 从匹配中所有已匹配的行和未匹配的列中选出尽可能少的顶点作为最小顶点覆盖
代码
from typing import List

def min_vertex_cover(grid: List[List[int]]) -> int:
    n, m = len(grid), len(grid[0])

    # 构建二分图
    graph = [[] for _ in range(n + m)]
    for i in range(n):
        for j in range(m):
            if grid[i][j] == 1:
                graph[i].append(n + j)
                graph[n + j].append(i)

    # 求最大匹配
    matching = [-1] * (n + m)
    def dfs(u: int) -> bool:
        for v in graph[u]:
            if not used[v]:
                used[v] = True
                if matching[v] == -1 or dfs(matching[v]):
                    matching[u] = v
                    matching[v] = u
                    return True
        return False

    for i in range(n):
        used = [False] * (n + m)
        dfs(i)

    # 计算顶点覆盖数
    cover = set()
    for i in range(n):
        if matching[i] == -1:
            cover.add(i)
    for j in range(n, n + m):
        if matching[j] != -1:
            cover.add(matching[j])

    return len(cover)

代码解释:

  • 行和列的编号分别为 0n-1nn+m-1
  • 图的存储方式为邻接表;
  • 使用 DFS 实现 Hopcroft-Karp 算法求最大匹配,其中 used 数组为 DFS 时用来记录某个顶点是否已被访问过;
  • 最后从匹配中所有已匹配的行和未匹配的列中选出顶点作为最小顶点覆盖,使用 set() 来去重。