带限制的最大子矩阵和(限制子矩阵面积至少为S)
字数 3660 2025-12-11 04:54:21

带限制的最大子矩阵和(限制子矩阵面积至少为S)

问题描述

给定一个 m × n 的整数矩阵 matrix 和一个整数 S,要求找出一个子矩阵,使得该子矩阵内所有元素的和最大,并且子矩阵的面积(即行数乘以列数)至少为 S。返回该最大和。

例如:

matrix = [
  [1, 2, -1],
  [3, 4,  5],
  [-2, 0, 2]
]
S = 4

可能的子矩阵之一是整个矩阵(面积=9,和=1+2-1+3+4+5-2+0+2=14),但题目要求面积至少为4,因此允许取部分子矩阵。其中一个满足面积至少4且和最大的子矩阵是:

[3, 4]
[-2, 0]

面积=2×2=4≥S,和=3+4+(-2)+0=5。但这是最大吗?检查所有子矩阵后,最大和是取整个矩阵的和14,因为其面积9≥4,且和14最大,所以答案是14。

实际上,当S较小时,最大和往往对应面积较大的子矩阵(因为可以包含更多正数)。但如果矩阵中有很多负数,则需要在面积约束和元素和之间权衡。

题目本质:在二维矩阵中,找一个面积至少为S的连续子矩阵,使得元素和最大。


解题思路

这是一个二维动态规划(或者更准确说是二维前缀和 + 滑动窗口/枚举优化)的问题。
直接暴力枚举所有子矩阵需要 O(m²n²) 的时间(枚举左上角、右下角),在 m,n 较大时不可接受。我们需要更高效的方法。

核心步骤:

  1. 预处理二维前缀和

    • 定义 prefix[i][j] 表示原矩阵中从 (0,0)(i-1,j-1) 的子矩阵和(i,j 从1开始计数)。
    • 计算:prefix[i][j] = matrix[i-1][j-1] + prefix[i-1][j] + prefix[i][j-1] - prefix[i-1][j-1]
    • 这样可以在 O(1) 时间内得到任意子矩阵的和:
      子矩阵左上角 (r1,c1),右下角 (r2,c2)(0-based)的和为:
      sum = prefix[r2+1][c2+1] - prefix[r1][c2+1] - prefix[r2+1][c1] + prefix[r1][c1]
      
  2. 将问题转化为一维问题

    • 固定子矩阵的上下边界(行 r1r2),将这一多行合并成“一维数组”:
      colSum[c] = sum_{row=r1..r2} matrix[row][c]
      
      我们可以用前缀和快速得到:colSum[c] = prefix[r2+1][c+1] - prefix[r1][c+1] - prefix[r2+1][c] + prefix[r1][c](实际上可以直接用前缀和差值得到)。
    • 问题变为:在这个一维数组 colSum[0..n-1] 中,找一个长度至少为 L 的连续子数组,使得和最大,且 L 满足:子矩阵的列数≥ ceil(S / (r2-r1+1))(因为面积=行数×列数≥S)。
  3. 在一维数组中找长度至少为L的最大和子数组

    • 设当前行数为 k = r2 - r1 + 1,则要求列数 cols ≥ ceil(S / k)
    • 我们令 minCols = ceil(S / k)
    • 问题:在数组 A[0..n-1](即 colSum)中,找长度至少为 minCols 的连续子数组的最大和。
    • 这可以用 前缀和 + 维护最小前缀和 的方法解决:
      1. 计算前缀和 pref[j] = sum(A[0..j-1])pref[0]=0
      2. 遍历 jminColsn
        • 当前子数组结尾于 j-1,长度为 len(len ≥ minCols),其和为 pref[j] - pref[j-len]
        • 对所有可能的 len≥minCols,我们只需维护 pref[j-minCols] 之前的最小前缀和 minPref(包括 pref[0..j-minCols]),那么以 j 结尾的满足长度≥minCols 的最大子数组和为 pref[j] - minPref
        • 更新 minPref = min(minPref, pref[j-minCols+1]) 注意下标。
          正确实现时,我们让 jminColsn,维护 minPref = min(minPref, pref[j-minCols]) 即可。
  4. 枚举行边界并求最大值

    • 枚举所有可能的 r1r2(O(m²)),对每一对行,用 O(n) 时间求一维问题的解。
    • 总复杂度 O(m² × n),在 m 和 n 约 200 时可行。
    • 若 m>n,可以转置矩阵,保证 O(min(m,n)² × max(m,n))。

详细算法步骤

  1. 输入矩阵 matrix(m 行 n 列)和整数 S。
  2. 计算二维前缀和 prefix,大小为 (m+1)×(n+1)
  3. 初始化答案 ans = -inf
  4. 枚举上边界 r1 从 0 到 m-1:
    • 枚举下边界 r2 从 r1 到 m-1:
      • 行数 k = r2 - r1 + 1
      • 最小所需列数 minCols = ceil(S / k)。若 minCols > n,则此 k 不可能满足面积≥S,跳过。
      • 构造一维数组 colSum[0..n-1],其中 colSum[c] = 从 r1 到 r2 行第 c 列的和,用二维前缀和公式快速计算。
      • 计算一维数组 colSum 的前缀和 pref[0..n]pref[0]=0
      • 初始化 minPref = pref[0]
      • 对于 jminColsn
        • currentSum = pref[j] - minPref
        • 更新 ans = max(ans, currentSum)
        • 更新 minPref = min(minPref, pref[j-minCols+1])(注意:j-minCols+1 可能会超出范围吗?不,因为 j≥minCols,j-minCols+1≥1,且 j-minCols+1 ≤ n-minCols+1 ≤ n,但 pref 长度 n+1,所以安全)。
      • 注意:当 minCols=0 时(S=0 时可能),但面积至少 S,S=0 相当于无面积限制,我们直接求最大子矩阵和即可(此时 minCols=0,一维问题退化为普通最大子数组和,用 Kadane 算法)。
  5. 返回 ans。

例子演示

例:

matrix = [[2, 1, -3],
          [0, 6,  5]]
S = 3
  1. 前缀和:
prefix:
0 0 0 0
0 2 3 0
0 2 9 11
  1. 枚举:
  • r1=0, r2=0: k=1, minCols=ceil(3/1)=3。
    一维数组 colSum = [2, 1, -3]。
    pref = [0, 2, 3, 0]。
    j=3: currentSum=pref[3]-minPref=0-min(0,2,3)=0-0=0。
    ans=0。
    但长度刚好3,其实列数=3≥3,和=2+1-3=0。
  • r1=0, r2=1: k=2, minCols=ceil(3/2)=2。
    colSum = 第一列:2+0=2;第二列:1+6=7;第三列:-3+5=2 → [2,7,2]。
    pref=[0,2,9,11]。
    minPref=pref[0]=0。
    j=2: currentSum=pref[2]-0=9,更新 ans=9。
    minPref=min(0, pref[0])=0(j-minCols+1=2-2+1=1,pref[1]=2,但 minPref 还是 0)。
    j=3: currentSum=pref[3]-0=11,更新 ans=11。
    所以得到子矩阵是整个矩阵,面积=2×3=6≥3,和=11。
  • r1=1, r2=1: k=1, minCols=3>n=3? 不,minCols=3==n=3,可以。
    colSum=[0,6,5],pref=[0,0,6,11]。
    minPref=0。
    j=3: currentSum=11-0=11,更新 ans=11。
    最终 ans=11。

复杂度分析

  • 预处理前缀和:O(mn)。
  • 枚举行对:O(m²)。
  • 对每个行对,O(n) 时间求解一维问题。
  • 总复杂度 O(m²n) 或 O(mn²)(通过转置保证 m≤n)。
  • 空间复杂度 O(mn) 用于前缀和。

边界情况与注意事项

  1. S 可能为 0:此时面积至少 0 相当于无限制,就是经典的最大子矩阵和问题(可用 Kadane 思想在 O(m²n) 解决)。
  2. S 可能很大(如大于 m×n):此时无解(但题目通常保证有解,因为至少可以取整个矩阵)。
  3. 矩阵元素可能有负数,所以初始 ans 要设为 -inf。
  4. 一维问题中,minCols 可能为 0(当 S=0),此时需单独处理,或者用 Kadane 算法求最大子数组和(无长度限制)。
  5. 若 minCols>n,则该行数不可能满足面积≥S,直接跳过。

代码框架(Python)

import math

def maxSubmatrixSum(matrix, S):
    m, n = len(matrix), len(matrix[0])
    # 1. 二维前缀和
    prefix = [[0]*(n+1) for _ in range(m+1)]
    for i in range(1, m+1):
        for j in range(1, n+1):
            prefix[i][j] = matrix[i-1][j-1] + prefix[i-1][j] + prefix[i][j-1] - prefix[i-1][j-1]
    
    ans = float('-inf')
    # 2. 枚举上下行
    for r1 in range(m):
        for r2 in range(r1, m):
            k = r2 - r1 + 1
            minCols = (S + k - 1) // k  # ceil(S/k)
            if minCols > n:
                continue
            # 构建一维数组 colSum
            colSum = [0]*n
            for c in range(n):
                # sum of rows r1..r2 in column c
                colSum[c] = prefix[r2+1][c+1] - prefix[r1][c+1] - prefix[r2+1][c] + prefix[r1][c]
            # 一维问题:长度至少 minCols 的最大和子数组
            # 方法:前缀和+维护最小前缀和
            pref = [0]*(n+1)
            for c in range(1, n+1):
                pref[c] = pref[c-1] + colSum[c-1]
            minPref = pref[0]
            for j in range(minCols, n+1):
                current = pref[j] - minPref
                if current > ans:
                    ans = current
                # 更新 minPref 为 pref[j-minCols+1] 之前的最小值
                # 实际上我们维护的是 pref[0..j-minCols] 的最小值
                minPref = min(minPref, pref[j-minCols+1])
    return ans

总结

这道题结合了二维前缀和枚举优化带长度限制的一维最大子数组和三个知识点。通过固定行边界将二维问题降为一维,再巧妙利用前缀和与最小前缀和来满足长度约束,最终在合理时间复杂度内求解。掌握这个套路,可以解决很多类似“带限制的子矩阵”问题。

带限制的最大子矩阵和(限制子矩阵面积至少为S) 问题描述 给定一个 m × n 的整数矩阵 matrix 和一个整数 S ,要求找出一个子矩阵,使得该子矩阵内所有元素的和最大,并且子矩阵的面积(即行数乘以列数) 至少为 S 。返回该最大和。 例如: 可能的子矩阵之一是整个矩阵(面积=9,和=1+2-1+3+4+5-2+0+2=14),但题目要求面积至少为4,因此允许取部分子矩阵。其中一个满足面积至少4且和最大的子矩阵是: 面积=2×2=4≥S,和=3+4+(-2)+0=5。但这是最大吗?检查所有子矩阵后,最大和是取整个矩阵的和14,因为其面积9≥4,且和14最大,所以答案是14。 实际上,当S较小时,最大和往往对应面积较大的子矩阵(因为可以包含更多正数)。但如果矩阵中有很多负数,则需要在面积约束和元素和之间权衡。 题目本质 :在二维矩阵中,找一个面积至少为S的连续子矩阵,使得元素和最大。 解题思路 这是一个二维动态规划(或者更准确说是 二维前缀和 + 滑动窗口/枚举优化 )的问题。 直接暴力枚举所有子矩阵需要 O(m²n²) 的时间(枚举左上角、右下角),在 m,n 较大时不可接受。我们需要更高效的方法。 核心步骤: 预处理二维前缀和 定义 prefix[i][j] 表示原矩阵中从 (0,0) 到 (i-1,j-1) 的子矩阵和(i,j 从1开始计数)。 计算: prefix[i][j] = matrix[i-1][j-1] + prefix[i-1][j] + prefix[i][j-1] - prefix[i-1][j-1] 。 这样可以在 O(1) 时间内得到任意子矩阵的和: 子矩阵左上角 (r1,c1) ,右下角 (r2,c2) (0-based)的和为: 将问题转化为一维问题 固定子矩阵的上下边界(行 r1 到 r2 ),将这一多行合并成“一维数组”: 我们可以用前缀和快速得到: colSum[c] = prefix[r2+1][c+1] - prefix[r1][c+1] - prefix[r2+1][c] + prefix[r1][c] (实际上可以直接用前缀和差值得到)。 问题变为:在这个一维数组 colSum[0..n-1] 中,找一个长度至少为 L 的连续子数组,使得和最大,且 L 满足:子矩阵的列数≥ ceil(S / (r2-r1+1)) (因为面积=行数×列数≥S)。 在一维数组中找长度至少为L的最大和子数组 设当前行数为 k = r2 - r1 + 1 ,则要求列数 cols ≥ ceil(S / k) 。 我们令 minCols = ceil(S / k) 。 问题:在数组 A[0..n-1] (即 colSum )中,找长度至少为 minCols 的连续子数组的最大和。 这可以用 前缀和 + 维护最小前缀和 的方法解决: 计算前缀和 pref[j] = sum(A[0..j-1]) , pref[0]=0 。 遍历 j 从 minCols 到 n : 当前子数组结尾于 j-1 ,长度为 len (len ≥ minCols),其和为 pref[j] - pref[j-len] 。 对所有可能的 len≥minCols,我们只需维护 pref[j-minCols] 之前的最小前缀和 minPref (包括 pref[0..j-minCols] ),那么以 j 结尾的满足长度≥minCols 的最大子数组和为 pref[j] - minPref 。 更新 minPref = min(minPref, pref[j-minCols+1]) 注意下标。 正确实现时,我们让 j 从 minCols 到 n ,维护 minPref = min(minPref, pref[j-minCols]) 即可。 枚举行边界并求最大值 枚举所有可能的 r1 和 r2 (O(m²)),对每一对行,用 O(n) 时间求一维问题的解。 总复杂度 O(m² × n),在 m 和 n 约 200 时可行。 若 m>n,可以转置矩阵,保证 O(min(m,n)² × max(m,n))。 详细算法步骤 输入矩阵 matrix (m 行 n 列)和整数 S。 计算二维前缀和 prefix ,大小为 (m+1)×(n+1) 。 初始化答案 ans = -inf 。 枚举上边界 r1 从 0 到 m-1: 枚举下边界 r2 从 r1 到 m-1: 行数 k = r2 - r1 + 1 。 最小所需列数 minCols = ceil(S / k) 。若 minCols > n ,则此 k 不可能满足面积≥S,跳过。 构造一维数组 colSum[0..n-1] ,其中 colSum[c] = 从 r1 到 r2 行第 c 列的和 ,用二维前缀和公式快速计算。 计算一维数组 colSum 的前缀和 pref[0..n] , pref[0]=0 。 初始化 minPref = pref[0] 。 对于 j 从 minCols 到 n : currentSum = pref[j] - minPref 。 更新 ans = max(ans, currentSum) 。 更新 minPref = min(minPref, pref[j-minCols+1]) (注意: j-minCols+1 可能会超出范围吗?不,因为 j≥minCols,j-minCols+1≥1,且 j-minCols+1 ≤ n-minCols+1 ≤ n,但 pref 长度 n+1,所以安全)。 注意:当 minCols=0 时(S=0 时可能),但面积至少 S,S=0 相当于无面积限制,我们直接求最大子矩阵和即可(此时 minCols=0,一维问题退化为普通最大子数组和,用 Kadane 算法)。 返回 ans。 例子演示 例: 前缀和: 枚举: r1=0, r2=0: k=1, minCols=ceil(3/1)=3。 一维数组 colSum = [ 2, 1, -3 ]。 pref = [ 0, 2, 3, 0 ]。 j=3: currentSum=pref[ 3 ]-minPref=0-min(0,2,3)=0-0=0。 ans=0。 但长度刚好3,其实列数=3≥3,和=2+1-3=0。 r1=0, r2=1: k=2, minCols=ceil(3/2)=2。 colSum = 第一列:2+0=2;第二列:1+6=7;第三列:-3+5=2 → [ 2,7,2 ]。 pref=[ 0,2,9,11 ]。 minPref=pref[ 0 ]=0。 j=2: currentSum=pref[ 2 ]-0=9,更新 ans=9。 minPref=min(0, pref[ 0])=0(j-minCols+1=2-2+1=1,pref[ 1 ]=2,但 minPref 还是 0)。 j=3: currentSum=pref[ 3 ]-0=11,更新 ans=11。 所以得到子矩阵是整个矩阵,面积=2×3=6≥3,和=11。 r1=1, r2=1: k=1, minCols=3>n=3? 不,minCols=3==n=3,可以。 colSum=[ 0,6,5],pref=[ 0,0,6,11 ]。 minPref=0。 j=3: currentSum=11-0=11,更新 ans=11。 最终 ans=11。 复杂度分析 预处理前缀和:O(mn)。 枚举行对:O(m²)。 对每个行对,O(n) 时间求解一维问题。 总复杂度 O(m²n) 或 O(mn²)(通过转置保证 m≤n)。 空间复杂度 O(mn) 用于前缀和。 边界情况与注意事项 S 可能为 0:此时面积至少 0 相当于无限制,就是经典的最大子矩阵和问题(可用 Kadane 思想在 O(m²n) 解决)。 S 可能很大(如大于 m×n):此时无解(但题目通常保证有解,因为至少可以取整个矩阵)。 矩阵元素可能有负数,所以初始 ans 要设为 -inf。 一维问题中,minCols 可能为 0(当 S=0),此时需单独处理,或者用 Kadane 算法求最大子数组和(无长度限制)。 若 minCols>n,则该行数不可能满足面积≥S,直接跳过。 代码框架(Python) 总结 这道题结合了 二维前缀和 、 枚举优化 和 带长度限制的一维最大子数组和 三个知识点。通过固定行边界将二维问题降为一维,再巧妙利用前缀和与最小前缀和来满足长度约束,最终在合理时间复杂度内求解。掌握这个套路,可以解决很多类似“带限制的子矩阵”问题。