📌  相关文章
📜  矩阵的最大总和,其中每个值都来自唯一的行和列(1)

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

矩阵的最大总和,其中每个值都来自唯一的行和列

给定一个由非负整数组成的矩阵,我们要找到一条路径,使得路径上的每个元素都来自矩阵的唯一行和列,且路径上元素的和尽可能大。

问题描述

给定一个 $m\times n$ 的矩阵 $M$,任意两个元素都不相同。

我们要求找到矩阵 $M$ 中的一个路径 $p$,满足:

  1. 路径 $p$ 由一个起点和一个终点组成。

  2. 路径 $p$ 的每一个元素都是矩阵 $M$ 中的某一行或某一列中的元素。

  3. 路径 $p$ 中的每个元素都来自于不同的行或列。

  4. 路径 $p$ 上元素的总和最大。

请编写一个函数来计算矩阵的最大总和,并返回该路径。

示例
输入:
[
  [5,  7,  2,  8],
  [3,  0,  9,  4],
  [6,  6,  2,  8]
]

输出:[3, 1, 0, 2, 3]
解法

我们可以将这个问题模型化成最大权匹配问题。

一个行与列相互匹配,意味着这个路径经过矩阵中的一对位置。假设我们可以给每个行和列赋予一个权重,那么我们的任务就是去找到一些一对一的匹配,满足所有的匹配的权重和最大。对于一个可能的匹配,其权重等于行权重与列权重之和。

注意到此问题是二分图最大权匹配问题,可以用匈牙利算法来解决。

具体地,在执行匈牙利算法时,在邻接矩阵的每个位置建立一个点。

每个行点 $i$ 连向其所在行的位置,每个列点 $j$ 连向其所在列的位置。若位置 $(i, j)$ 上的值为 $w_{i, j}$,则从行点 $i$ 到列点 $j$ 连接一条权值为 $w_{i, j}$ 的边。

最后得到的匹配中,行点 $i$ 与列点 $match[i]$ 所在的列之间是匹配的。注意到如果匹配不全,我们需要较少列点的数量以匹配行点。

算法

假设矩阵 $M$ 的维度是 $m\times n$。

时间复杂度

建立邻接矩阵需要 $O(mn)$,匈牙利算法的时间复杂度为 $O(m^2n)$。因此总时间复杂度为 $O(m^2n)$。

空间复杂度

建立邻接矩阵需要 $O(mn)$ 的空间,匈牙利算法的空间复杂度为 $O(m+n)$。因此总空间复杂度为 $O(mn)$。

代码
from typing import List

class Solution:
    def maxSumOfMatrix(self, matrix: List[List[int]]) -> List[int]:
        m, n = len(matrix), len(matrix[0])
        row_weights = [max(row) for row in matrix]
        col_weights = [0] * n

        # 建立邻接矩阵
        adj = [[0] * n for _ in range(m)]
        for i in range(m):
            for j in range(n):
                adj[i][j] = row_weights[i] + col_weights[j] - matrix[i][j]
        
        # 匈牙利算法
        match = [-1] * m
        vis1, vis2 = [False] * m, [False] * n

        def dfs(u: int) -> bool:
            vis1[u] = True
            for v in range(n):
                if not vis2[v] and adj[u][v] == 0:
                    vis2[v] = True
                    if match[v] == -1 or dfs(match[v]):
                        match[v] = u
                        return True
            return False

        for i in range(m):
            vis1 = [False] * m
            dfs(i)
        
        return [match[i], i, i, match[i+1], i+1 for i in range(0, len(match), 2)]