Leetcode【固定左右边界 前缀和/动态规划】 | 363. 矩形区域不超过K的最大数值和(JAVA详细注释)

这篇博客介绍了如何解决LeetCode中的363题,即寻找二维矩阵中不超过K的最大矩形和。通过固定左右边界,将问题转化为求一维数组最大子序和。博客探讨了四种解法:暴力枚举、前缀和、动态规划以及前缀和结合二分查找。重点讲述了动态规划和前缀和+二分法的优化思路。
摘要由CSDN通过智能技术生成

题目

给定一个非空二维矩阵 matrix 和一个整数 k,找到这个矩阵内部不大于 k 的最大矩形和。
示例:
 输入: matrix = [[1,0,1],[0,-2,3]], k = 2
 输出: 2
解释: 矩形区域 [[0, 1], [-2, 3]] 的数值和是 2,且 2 是不超过 k 的最大数字(k = 2)。
说明:
 矩阵内的矩形区域面积必须大于 0。
 如果行数远大于列数,你将如何解答呢?
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/max-sum-of-rectangle-no-larger-than-k

解题

思路(固定左右边界求矩形最大和将其转化为一维数组最大子序和问题)

 对matrix = [[5,-4,-3,4],[-3,-4,4,5],[5,1,5,-4]]的矩阵来说,如下图,要选择其中子矩阵的小于K的最大矩阵和。最直观的思路就是枚举所有的(左边界,右边界)和(上边界,下边界),然后求这四个边界内的元素的和,和的求法可以是每一行相加也可以是每一列相加。
 暴力法来说,循环一般写成先枚举一对边界的组合,再枚举另一对边界的组合。因为枚举一对边界就是两层循环,这样下来是四层循环。我们要想办法简化问题。

在这里插入图片描述
 我们可以固定一对边界(因为行数远大于列数,所以先枚举固定左右边界),然后不用枚举另一种边界的组合可以通过这样的方式来完成:固定左右边界后,上下边界组合下的矩阵的和我们可以通过该左右边界下每一行的和的方式来计算。
 所以我们枚举不同的左右边界组合,对每一种左右边界来说,求该边界条件下其每一行的和用rowsum[row.length]存储。这样枚举上下边界并求该上下左右边界构成的矩形最大和的方式就变成了求rowsum[]数组的最大子序和的问题。这样既完成了上下边界的枚举,又相当于将二维数组压缩成了一维数组来求解。如下图示例,当左右边界为[0,2]时,rowsum[] = {-2,-3,11}。问题就变成求解rowsum数组的小于K的最大子序和的求解了。如下给出了几种解法。
在这里插入图片描述
此时的矩形和求解方法例如下面这种直接求法:
在这里插入图片描述

rowsum[]的小于K的最大子序和解法

暴力求法

 枚举所有情况,对连续的子数组进行依次累加维护小于K的最大值即可。

class Solution {
   
    public int maxSumSubmatrix(int[][] matrix, int k) {
   
        //固定左右边界,求出该左右边界内每行的和用rowsum[]存储,只考虑哪两个行之间的矩形和最大,就变成了求rowsum中最大子序和的问题
        //1、划定左右边界,求每行和;2、找不超过K的两行的组合(即对应矩阵)的最大值
        //为什么用列划分,因为行数远超于列数
        int row = matrix.length;//行数
        //非空矩阵不用判断特殊情况
        int col = matrix[0].length;//列树
        int res = Integer.MIN_VALUE;//初始化结果为最小值
        for(int l = 0; l < col;l++){
   //l为左边界
            int[] rowsum = new int[row];//每一行的和的数组
            for(int r = l; r < col; r++){
   //r为有边界
                for(int i = 0; i < row; i++){
   
                    //求当前左右边界[l,r]时每行的和的前缀和,以便求不同行之间的矩形和
                    rowsum[i] += matrix[i][r];
                }
                //求[l,r]边界下矩阵不超过k的最大矩形和
                res = Math.max(res,lrmax(rowsum,k));
                if(res == k) return k;//尽量减少运算
            } 
        }
        return res;
    }
    //求[l,r]边界下矩阵不超过k的最大矩形和,变成rowsum数组的小于K的最大子序和问题
    public int lrmax(int[] rowsum, int k){
   
        //暴力法 O(row^2)
        int max = Integer.MIN_VALUE;
        for(int i = 0; i < rowsum.length; i++){
   
            int sum = 0;
            for(int j = i; j < rowsum.length;j++){
   
                //i到j行之间的和就是依次叠加
                sum += rowsum[j];
                if(sum<=k && sum>max) max = sum;
                if(max == k) return k;
            }
        }
        return max;
    }
}

在这里插入图片描述

前缀和求法

在小于K的最大子序和的求解上,采用前缀和,前缀和详解具体看leetcode【前缀和】 | 560. 和为K的子数组

class Solution {
   
    public int maxSumSubmatrix(int[][] matrix, int k) {
   
        //固定左右边界,求出该左右边界内每行的和用rowsum[]存储,只考虑哪两个行之间的矩形和最大,就变成了求rowsum中最大子序和的问题,用前缀和求(参考"leetcode 560. 和为K的子数组")
        //1、划定左右边界,求每行和;2、找不超过K的两行的组合(即对应矩阵)的最大值
        //为什么用列划分,因为行数远超于列数
        int row = matrix.length;//行数
        //非空矩阵不用判断特殊情况
        int col = matrix[0].length;//列树
        int res = Integer.MIN_VALUE;//初始化结果为最小值
        for(int l = 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值