【前缀和】A011_LC_矩形区域不超过 K 的最大数值和 & 最大子矩阵(暴力前缀和 / 固定左右边界 + 二分)

一、矩形区域不超过 K 的最大数值和

给定一个非空二维矩阵 matrix 和一个整数 k,找到这个矩阵内部不大于 k 的最大矩形和。

输入: matrix = [[1,0,1],[0,-2,3]], k = 2
输出: 2 
解释: 矩形区域 [[0, 1], [-2, 3]] 的数值和是 2,且 2 是不超过 k 的最大数字(k = 2)。

说明:

矩阵内的矩形区域面积必须大于 0。
如果行数远大于列数,你将如何解答呢?

方法一:裸前缀和

思路

class Solution {
public:
    int maxSumSubmatrix(vector<vector<int>>& g, int k) {
    	int n = g.size(), m = g[0].size(), ans = INT_MIN, s[n+1][m+1]; memset(s, 0, sizeof s);
    	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++) {
			s[i][j] = s[i-1][j] + s[i][j-1] - s[i-1][j-1] + g[i-1][j-1];
		}
		
		for (int x1 = 0; x1 <= n; x1++)
		for (int y1 = 0; y1 <= m; y1++)
		for (int x2=x1+1; x2 <= n; x2++)
		for (int y2=y1+1; y2 <= m; y2++) {
			int sum = s[x2][y2] - s[x1][y2] - s[x2][y1] + s[x1][y1];
			if (sum <= k && sum > ans) ans = sum;
		}
		return ans;		
    }
};

不太理想的一次 ac

执行用时:1068 ms
内存消耗:8.5 MB

复杂度分析

  • 时间复杂度: O ( n 4 ) O(n^4) O(n4)
  • 空间复杂度: O ( n 2 ) O(n^2) O(n2)

方法二:固定左右边界 + 二分

Q:如果行数远大于列数,你将如何解答呢?

别人的优秀思路:固定两个左右边界(矩阵的左右边界),然后从小到大枚举行,在枚举行的同时,用 set 存储每一行得到的前缀和的累加值 tot

下一次枚举时,假如当前行的前缀和值为 sum,总共为 tot,我们只需在 set 中二分找到一个值 v ( 满 足   v ⩾ t o t − k , 即   t o t − v ⩽ k ) v(满足\ v \geqslant tot-k,即\ tot-v \leqslant k) v vtotk totvk

class Solution {
    public int maxSumSubmatrix(int[][] g, int k) {
        int n = g.length, m = g[0].length, ans = Integer.MIN_VALUE, s[][] = new int[n+1][m+1];
        for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++) {
            s[i][j] = s[i][j-1] + g[i-1][j-1];
        }
        
        for (int l = 0; l <= m; l++)
        for (int r = l+1; r <= m; r++) {
            TreeSet<Integer> st = new TreeSet<>(); st.add(0);
            int tot = 0;
            for (int i = 1; i <= n; i++) {
                tot += s[i][r]-s[i][l];
                Integer v = st.ceiling(tot-k);
                if (v != null)
                    ans = Math.max(ans, tot-v);
                st.add(tot);
            }
        }
        return ans;     
    }
}

ceiling 方法对应 C++ set 的 lower_bound

复杂度分析

  • 时间复杂度: O ( m 2 × n × l o g n ) O(m^2 × n × logn) O(m2×n×logn)
  • 空间复杂度: O ( n m ) O(nm) O(nm)

二、最大子矩阵

给定一个正整数和负整数组成的 N × M 矩阵,编写代码找出元素总和最大的子矩阵。

返回一个数组 [r1, c1, r2, c2],其中 r1, c1 分别代表子矩阵左上角的行号和列号,r2, c2 分别代表右下角的行号和列号。若有多个满足条件的子矩阵,返回任意一个均可。

注意:本题相对书上原题稍作改动

输入:
[
   [-1,0],
   [0,-1]
]
输出: [0,1,0,1]
解释: 输入中标粗的元素即为输出所表示的矩阵

说明:

1 <= matrix.length, matrix[0].length <= 200

方法一:逻辑优化

n 2 × m 2 n^2 × m^2 n2×m2 暴力不难想…

思路

该优化解法比较有趣,利用的是最大字段和思想:当前面的 A [ i . . . j ] A[i...j] A[i...j] 累加和 s < 0 s < 0 s<0 时,我们应该舍弃这一段前缀和,从 j + 1 j+1 j+1 位置重新开始统计

算法

  • 固定矩阵的首列、尾列下标 l , r l,r lr,然后枚举行 i i i
  • 在枚举的时候,如果发现前缀和 s < 0 s < 0 s<0 的话,此时应该更新 s i si si,也就是对应上述的思想( s i si si 子矩阵的首行下标、 i i i 为尾行下标)
  • 如果此时的前缀和 s 大于历史最大值 mx,应该更新 vector<int> ans

这样我只需要 O ( m 2 × n ) O(m^2 × n) O(m2×n)

class Solution {
public:
    vector<int> getMaxMatrix(vector<vector<int>>& g) {
		int n = g.size(), m = g[0].size(), mx = INT_MIN, s[n+1][m+1]; memset(s, 0, sizeof s);
    	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
			s[i][j] = s[i][j-1] + g[i-1][j-1];

		vector<int> ans;
		for (int l = 0; l < m; l++)
		for (int r=l+1; r <= m; r++) {
			int cur = 0, si = 1;
			for (int i = 1; i <= n; i++) {
				if (cur < 0) {
					cur = s[i][r]-s[i][l], si = i;
				} else {
					cur += s[i][r]-s[i][l];
				}
                if (cur > mx)
                    mx = cur, ans = {si-1, l, i-1, r-1};
			}
		}
		return ans;
    }
};

复杂度分析

  • 时间复杂度: O ( m 2 × n ) O(m^2 × n) O(m2×n)
  • 空间复杂度: O ( m n ) O(mn) O(mn)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值