LeetCode每日一题(Max Sum of Rectangle No Larger Than K)

本文介绍了如何解决一个二维数组中找到最大子矩阵和不超过k的问题。通过计算每行的前缀和,然后进一步计算整个二维数组的前缀和矩阵,可以有效地找出满足条件的子矩阵的最大和。文章提供了具体的算法实现,虽然采用的是暴力方法,但已成功通过了所有测试用例。

Given an m x n matrix matrix and an integer k, return the max sum of a rectangle in the matrix such that its sum is no larger than k.

It is guaranteed that there will be a rectangle with a sum no larger than k.

Example 1:

Input: matrix = [[1,0,1],[0,-2,3]], k = 2
Output: 2
Explanation: Because the sum of the blue rectangle [[0, 1], [-2, 3]] is 2, and 2 is the max number no larger than k (k = 2).

Example 2:

Input: matrix = [[2,2,-1]], k = 3
Output: 3

Constraints:

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

思路:

一维的presum想必大家都用过, arr=[1, 2, 3, 4, 5],  presum=[0, 1, 3, 6, 10, 15]。那二维数组能不能做presum?答案是能。

vec=[

[1, 0, 1]

[0, -2, 3]

[0, 1, 0]

]

我们先计算每行的row_presum = [

[0, 1, 1, 2]

[0, 0, -2, 1]

[0, 0, 1, 1]

]

在根据row_presum每一列累加,得到presum=[

[0, 0, 0, 0, 0]

[0, 0, 1, 1, 2]

[0, 0, 1, -1, 3]

[0, 0, 1, 0, 4]

], 这样presum[i][j]实际表示的是从vec[0][0]到vec[i-1][j-1]这个矩阵的和。有了这个我们计算里面任何一个子矩阵的和就简单多了. 比如我们要计算从vec[1][1]到vec[2][2]这个矩阵的和,我们可以用vec[0][0]到vec[2][2]的和 - vec[0][0]到vec[2][0]的和 - vec[0][0]到vec[0][2]的和 + vec[0][0]到vec[0][0]的和, 之所以最后要加一项是因为前面的两个减项有重叠的部分,而重叠的部分就是vec[0][0]到vec[0][0], 由此我们可以推出, 假设我们把起始点的索引记为Si, Sj, 终止点的索引记为Ei, Ej, 那我们可以推出sum(Si, Sj, Ei, Ej) = presum[Ei+1][Ej+1] - presum[Si][Ej+1] - presum[Ei+1][Sj] + presum[Si][Sj]。求和公式推倒出来了,剩下的就是怎么用了,我是没想出好办法,只能暴力来,外层遍历Ei, Ej, 内层根据Si <= Ei, Sj <= Ej再便利Si, Sj, 复杂度应该是O(n2), 但是提交的时候也通过了. 

代码(Rust):


impl Solution {
    pub fn max_sum_submatrix(matrix: Vec<Vec<i32>>, k: i32) -> i32 {
        let row_presum: Vec<Vec<i32>> = matrix
            .iter()
            .map(|row| {
                row.iter().fold(vec![0], |mut l, v| {
                    let last = l.last().unwrap();
                    l.push(*v + *last);
                    l
                })
            })
            .collect();
        let mut presum = vec![vec![0; matrix[0].len() + 1]; matrix.len() + 1];
        for col in 1..matrix[0].len() + 1 {
            let mut sum = 0;
            for row in 1..matrix.len() + 1 {
                sum += row_presum[row - 1][col];
                presum[row][col] = sum;
            }
        }
        let area = |srow: usize, scol: usize, erow: usize, ecol: usize| -> i32 {
            presum[erow + 1][ecol + 1] - presum[erow + 1][scol] - presum[srow][ecol + 1]
                + presum[srow][scol]
        };
        let mut max = -10001;
        for end_row in 0..matrix.len() {
            for end_col in 0..matrix[0].len() {
                for start_row in 0..=end_row {
                    for start_col in 0..=end_col {
                        let a = area(start_row, start_col, end_row, end_col);
                        if a > k {
                            continue;
                        } else {
                            max = max.max(a);
                        }
                    }
                }
            }
        }
        max
    }
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值