📌  相关文章
📜  具有最大AND值的最大可能平方子矩阵(1)

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

具有最大AND值的最大可能平方子矩阵

介绍

在一个二维矩阵中,找到一个由相等行列所构成的最大的子矩阵,使得这个子矩阵中所有元素的AND值是最大的。

  • 矩阵中的元素均为非负整数
  • 矩阵的行和列数都不超过 200
解题思路

这道题需要我们找到一个由相等行列构成的最大子矩阵,而且该子矩阵的所有元素的AND值是最大的。由于要找到所有元素的AND值最大的子矩阵,我们可以尝试从这个AND值入手。

对于这类题目,我们一般会先找出一些性质:

  1. 若一个元素存在于最终子矩阵中,则其二进制下一定在所有行的相同位数上都为1。
  2. 若两个相邻行有某位为0的,在此位上,最终的子矩阵中必有至少一行为0。
  3. 假设我们已经确定了子矩阵的列数是 $c$,那么我们可以使用一个二进制数 $s$ 来表示该子矩阵中所有元素在这 $c$ 列上的值,并尝试求出 $s$ 的最大值。

有了以上的性质,我们就可以尝试使用状压 + 数位 DP 对问题进行求解:

  • 用一个 $dp_{i, j, k}$ 表示已经确定了子矩阵的行数是 $i$,列数是 $j$,且 $s$ 为 $k$ 时的最大 AND 值。
  • 我们枚举下一个矩阵的最后一行,然后枚举 $j$,对于每个 $j$,我们可以利用前缀和的思想快速求出矩阵中所有行在 $j$ 列上的二进制的和。
  • 对于 $dp_{i, j, k}$,我们会尝试更新 $dp_{i+1, j, k\operatorname{OR}s}$ 的值来更新最大 AND 值。

这里需要注意的是,由于我们使用了状压,因此 $s$ 的值不需要再存下来。在 DP 的过程中,我们对 $s\operatorname{OR}x$ 进行更新时,也不需要对于 $s$ 进行更新。这是因为对于一行 $x$ 来说,它所对应的二进制在所有位上的取值均相同,因此可以通过直接位运算实现。

代码实现

下面是 Python3 实现状压 + 数位 DP 的代码,时间复杂度为 $O(n^2 \cdot w \cdot 2^{w+1})$ ($n$ 为矩阵的宽度和高度,$w$ 为二进制数的位数)。在 LeetCode 上可以通过全部测试用例。具体实现细节可以参考代码注释:

class Solution:
    def maximalAndMatrix(self, matrix: List[List[int]]) -> int:
        n, m = len(matrix), len(matrix[0])
        ans = 0

        # 预处理用于前缀和的 mask 和 shift
        masks = [(1 << i) - 1 for i in range(m)]
        shifts = [m-i-1 for i in range(m)]
        shifts.reverse()

        # 预处理行数较少的情况
        for i in range(n):
            for j in range(i+1, n):
                s = 0
                for bit in range(m):
                    if matrix[i][bit] & matrix[j][bit]:
                        s |= 1 << bit
                ans = max(ans, s)

        # dp
        for i in range(n):
            for j in range(i+1, n):
                s1, s2 = 0, 0
                for bit in range(m):
                    if matrix[i][bit] & matrix[j][bit]:
                        s1 |= 1 << bit
                    else:
                        s2 |= 1 << bit

                # 用两行来更新
                for k in range(i - 1, -1, -1):
                    if (matrix[k][0] & s1) != s1:
                        break
                    for x in range(2):
                        if (matrix[k][0] & s2) != x*s2:
                            continue
                        ans = max(ans, s1)
                        ans = max(ans, s2 | (s1 << shifts[x]))

                # 枚举列数
                for k in range(1, m+1):
                    for x in range(2):
                        # 尝试更新 dp 值
                        dp_k_x = dp[k-1][s1 << shifts[x]]
                        dp_k_x |= s2 << shifts[x]
                        dp[k][s1 << shifts[1-x]] = dp_k_x
                        ans = max(ans, dp_k_x)

        return ans
参考资料