在问 · 算法题解

363. 矩形区域不超过 K 的最大数值和

困难二分查找枚举上下边界 + 有序集合力扣原题 ↗

题目描述

输入一个 m x n 的整数矩阵 matrix 和一个整数 k。矩阵中任意由连续若干行、连续若干列围成的区域都算一个矩形,矩形的和是其中所有元素之和。

在所有矩形中,只看和不超过 k 的那些,返回其中最大的和。

题目保证至少有一个矩形的和不超过 k。

示例 1
输入:matrix = [[1,0,1],[0,-2,3]], k = 2
输出:2

解释:取右侧两列构成的矩形 [[0,1],[-2,3]],和为 2,恰好等于 k。

示例 2
输入:matrix = [[2,2,-1]], k = 3
输出:3

解释:取前两个元素 [2,2] 得到 4 超过了 k,取 [2,2,-1] 和为 3。

数据范围

  • m == matrix.length
  • n == matrix[i].length
  • 1 <= m, n <= 100
  • -100 <= matrix[i][j] <= 100
  • -10^5 <= k <= 10^5

进阶:如果行数远多于列数,应怎样调整解法?

解题思路:枚举上下边界 + 有序集合

枚举上边界 top 和下边界 bot,把这几行按列加起来,压成一维数组 col。问题变成:在 col 中找和不超过 k 的最大子数组和。子数组和 = cur - pre(两个前缀和之差),要求 cur - pre <= k,即 pre >= cur - k;为了让差尽量大,pre 要尽量小。所以把过去的前缀和放进有序列表 seen,用 bisect_left 找第一个 >= cur - k 的前缀和。

为什么正确

处理到第 c 列时,seen 是前缀和 P0=0, P1, …, Pc 的有序版本;best 是已检查过的子矩形中不超过 k 的最大和。

复杂度分析

时间 O(m² · n log n)(列表插入在 Python 里是 O(n),可用平衡树做到 log n);空间 O(n)。如果行比列多,可以转置后再做。

易错点

要找的是“第一个 >= cur - k”的前缀和(bisect_left),不是最接近 cur 的。seen 初始要放入 0,代表从第 0 列开始的子数组。

参考代码

Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。

class Solution:
    def maxSumSubmatrix(self, matrix: List[List[int]], k: int) -> int:
        m, n = len(matrix), len(matrix[0])
        best = -10**9
        for top in range(m):
            col = [0] * n
            for bot in range(top, m):
                for c in range(n):
                    col[c] += matrix[bot][c]
                seen = [0]
                cur = 0
                for v in col:
                    cur += v
                    i = bisect_left(seen, cur - k)
                    if i < len(seen):
                        best = max(best, cur - seen[i])
                    insort(seen, cur)
        return best

载入编辑器,自己跑一遍 →

其他解法

暴力枚举矩形的四条边,O(m²n²);主解法只枚举上下边,把左右边的选择变成一维「不超过 k 的最大子数组和」,用有序集合二分将一维降到 log。

解法 2:二维前缀和 + 枚举所有矩形 时间 O(m² n²),空间 O(mn)

先求二维前缀和 pre,使任意矩形的和都能 O(1) 算出:以 (r1, c1) 为左上角、(r2, c2) 为右下角的和是 pre[r2+1][c2+1] - pre[r1][c2+1] - pre[r2+1][c1] + pre[r1][c1]。枚举所有左上角和右下角,记录不超过 k 的最大和。

思路简单,不需要有序集合。矩形个数约为 m²n²/4,在 100×100 的数据范围下约 2.5×10⁷ 次运算,编译型语言勉强可用,Python 容易超时。适合作为对拍基准。

class Solution:
    def maxSumSubmatrix(self, matrix: List[List[int]], k: int) -> int:
        m, n = len(matrix), len(matrix[0])
        pre = [[0] * (n + 1) for _ in range(m + 1)]
        for i in range(m):
            for j in range(n):
                pre[i + 1][j + 1] = pre[i][j + 1] + pre[i + 1][j] - pre[i][j] + matrix[i][j]
        best = -10**9
        for r1 in range(m):
            for r2 in range(r1, m):
                for c1 in range(n):
                    for c2 in range(c1, n):
                        s = pre[r2 + 1][c2 + 1] - pre[r1][c2 + 1] - pre[r2 + 1][c1] + pre[r1][c1]
                        if best < s <= k:
                            best = s
        return best

二分查找 专题的其他题目

查看 二分查找 全部题目 →