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

本文提供LeetCode第363题“矩形区域不超过K的最大数值和”的解决方案,采用枚举列边界并将问题转化为一维数组处理的方法,通过前缀和与平衡树实现高效求解。

7effe9187819b317beaa2cd9bbaf2dc3.png

祖传的手艺不想丢了,所以按顺序写一个leetcode的题解。计划每日两题,争取不卡题吧

363.矩形区域不超过K的最大数值和https://leetcode-cn.com/problems/max-sum-of-rectangle-no-larger-than-k/

最朴素的做法当然是枚举左上角和右下角的坐标,然后计算其中的数值和并判断与k的大小情况,是O((mn)^2)的算法。

由于题目后面有说到行数远大于列数的情况,因此自然的想法就是通过枚举固定住列的两侧,然后在行上面做文章。

当列固定住之后,原来二维矩阵的题目就可以通过累加和转化为一维数组上的问题,即对于一个长度为m的数组nums,需要选择两点i和j,使得nums[i] + nums[i + 1] + ... + nums[j]小于等于k,且最大。

注意到上面涉及到了区间和的运算,于是一个自然的想法就是将其进一步转化为前缀和的问题,那么现在就变成了对于前缀和数组sum_list,需要选择两点i和j,是的sum_list[j] - sum_list[i - 1]小于等于k,且最大。

这个问题似乎在哪里见到过,不过遗憾的是,这里有一点不同时数值中存在负数,因此sum_list并不是单调递增的,因此不能通过滑动窗口来做。

在这里,对于一个sum_list[j],我们的目标是找到对应的i,其满足sum_list[i - 1] >= sum_list[j] - k,sum_list[i - 1]是最大的那一个。

于是一个自然的想法就是一趟扫描,通过平衡树来维护之前的sum_list。设当前处理到sum_list[j],那么每次先在平衡树中查找比sum_list[j] - k小的最大的值并对比其与答案的大小,然后将sum_list[j]插入到平衡树中。

最终的时间复杂度是O(mlogm),于是整个问题最终的时间复杂度就是O(n^2 * mlogm)。

我在这里又是使用的数组加二分查找的方式(主要还是对python的了解还不够深刻,可能存在某种类平衡树的东西我不知道用),这里数组插入的时间复杂度可以到O(m),因此最坏情况下时间复杂度还是O((mn)^2)。

当然,也可以加入一些先做最大子段和确定上界之类的剪枝操作,不过最坏情况下这些剪枝操作都不会影响到最终的时间复杂度,因此就不在这里展开了。

最后附上python代码:

class Solution(object):
    def maxSumSubmatrix(self, matrix, k):
        """
        :type matrix: List[List[int]]
        :type k: int
        :rtype: int
        """

        if not matrix:
            return 0

        m = len(matrix)
        n = len(matrix[0])

        res = -2147483647
        for left in range(n):
            row_sum = [0] * m
            for right in range(left, n):
                pre_sum = 0
                sum_list = []
                for index in range(m):
                    row_sum[index] += matrix[index][right]
                    pre_sum += row_sum[index]

                    if pre_sum <= k:
                        res = max(res, pre_sum)
                        if res == k:
                            return res

                    if not sum_list:
                        sum_list.append(pre_sum)
                    else:
                        tmp = pre_sum - k
                        if tmp <= sum_list[-1]:
                            l = 0
                            r = len(sum_list) - 1
                            while l < r:
                                mid = (l + r) / 2
                                if sum_list[mid] == tmp:
                                    return k
                                if sum_list[mid] < tmp:
                                    l = mid + 1
                                else:
                                    r = mid
                            res = max(res, pre_sum - sum_list[l])

                        if pre_sum > sum_list[-1]:
                            sum_list.append(pre_sum)
                        else:
                            l = 0
                            r = len(sum_list) - 1
                            insert_flag = True
                            while l < r:
                                mid = (l + r) / 2
                                if sum_list[mid] == pre_sum:
                                    insert_flag = False
                                    break
                                if sum_list[mid] < pre_sum:
                                    l = mid + 1
                                else:
                                    r = mid
                            sum_list = sum_list[:l] + [pre_sum] + sum_list[l:]

        return res
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值