1. 程式人生 > >【LeetCode】363. Max Sum of Rectangle No Larger Than K 解題報告(Python)

【LeetCode】363. Max Sum of Rectangle No Larger Than K 解題報告(Python)

題目描述:

Given a non-empty 2D matrix matrix and an integer k, find the max sum of a rectangle in the matrix such that its sum is no larger than k.

Example:

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

Note:

  1. The rectangle inside the matrix must have an area > 0.
  2. What if the number of rows is much larger than the number of columns?

題目大意

找出一個矩陣中的子長方形,使得這個長方形的和是最大的。

解題方法

方法一:暴力求解(TLE)

求和最大的矩形,很容易讓人想到先把(0, 0)到所有(i, j)位置的矩形的和求出來,然後再次遍歷,求出所有子矩形中和最大的那個。

很無奈,超時了。(好像C++可以通過,python傷不起)

時間複雜度是O((MN)^2),空間複雜度是O(MN)。

class Solution(object):
    def maxSumSubmatrix(self, matrix, k):
        """
        :type matrix: List[List[int]]
        :type k: int
        :rtype: int
        """
        if not matrix or not matrix[0]: return 0
        M, N = len(matrix), len(matrix[0])
        sums = [[0] * N for _ in range(M)]
res = float("-inf") for m in range(M): for n in range(N): t = matrix[m][n] if m > 0: t += sums[m - 1][n] if n > 0: t += sums[m][n - 1] if m > 0 and n > 0: t -= sums[m - 1][n - 1] sums[m][n] = t for r in range(m + 1): for c in range(n + 1): d = sums[m][n] if r > 0: d -= sums[r - 1][n] if c > 0: d -= sums[m][c - 1] if r > 0 and c > 0: d += sums[r - 1][c - 1] if d <= k: res = max(res, d) return res

方法二:Kadane’s algorithm (TLE)

看了印度小哥的視訊,真的很好理解,告訴我們使用一個數組的情況下,如何找出整個二維子矩陣的最大值。我看了視訊之後,寫出了這個演算法,但是很無奈,直接用這個演算法仍然超時。

我分析,這個演算法時間複雜度仍然沒有降下來,主要問題是獲取子陣列的最大區間和這一步太耗時了。

時間複雜度是O((MN)^2),空間複雜度是O(M)。

class Solution(object):
    def maxSumSubmatrix(self, matrix, k):
        """
        :type matrix: List[List[int]]
        :type k: int
        :rtype: int
        """
        if not matrix or not matrix[0]: return 0
        L, R = 0, 0
        curSum, maxSum = float('-inf'), float('-inf')
        maxLeft, maxRight, maxUp, maxDown = 0, 0, 0, 0
        M, N = len(matrix), len(matrix[0])
        for L in range(N):
            curArr = [0] * M
            for R in range(L, N):
                for m in range(M):
                    curArr[m] += matrix[m][R]
                curSum = self.getSumArray(curArr, M, k)
                if curSum > maxSum:
                    maxSum = curSum
        return maxSum
            
    def getSumArray(self, arr, M, k):
        sums = [0] * (M + 1)
        for i in range(M):
            sums[i + 1] = arr[i] + sums[i]
        res = float('-inf')
        for i in range(M):
            for j in range(i + 1, M + 1):
                curSum = sums[j] - sums[i]
                if curSum <= k and curSum > res:
                    res = curSum
        return res

方法二:Kadane’s algorithm + 二分查詢 (Accepted)

上面的演算法慢就慢在查詢子陣列的最大和部分。其實沒必要使用求最大和的方式。因為題目要求我們找出不超過K的和,所以只需要在陣列中是否存在另外一個數使得兩者的差不超過K即可。這個查詢的效率能達到O(NlogN).

在C++中能使用set和lowwer_bound實現,在python中使用bisect_left函式能也實現。

這個過程可以在這個文章中看到更詳細的說明。

在時間複雜度中可以看到M影響更大,另外一個優化的策略是重新設定矩形的長和寬,這樣也可以優化速度。

時間複雜度是O(MNMlogM),空間複雜度是O(M)。

class Solution(object):
    def maxSumSubmatrix(self, matrix, k):
        """
        :type matrix: List[List[int]]
        :type k: int
        :rtype: int
        """
        m = len(matrix)
        n = len(matrix[0]) if m else 0
        
        M = max(m, n)
        N = min(m, n)
        ans = None
        for x in range(N):
            sums = [0] * M
            for y in range(x, N):
                slist, num = [], 0
                for z in range(M):
                    sums[z] += matrix[z][y] if m > n else matrix[y][z]
                    num += sums[z]
                    if num <= k:
                        ans = max(ans, num)
                    i = bisect.bisect_left(slist, num - k)
                    if i != len(slist):
                        ans = max(ans, num - slist[i])
                    bisect.insort(slist, num)
        return ans or 0

參考資料:

日期

2018 年 10 月 11 日 —— 做Hard題真的很難