给你一个 m x n 的矩阵 matrix 和一个整数 k ,找出并返回矩阵内部矩形区域的不超过 k 的最大数值和。
题目数据保证总会存在一个数值和不超过 k 的矩形区域。
最近做的全是矩阵的面积有关的问题,还是一样的,通过枚举子矩阵然后计算每一列的前缀和将二维问题转换为一维数组中的问题
- 因此通过计算每一列的前缀和将问题转换为:给定一个整数数组和一个整数k,计算该数组的最大区间和,要求区间和不超过k
这里注意两个TreeSet中的方法,简直是无敌般的存在:
- floor(E e) 方法返回在这个集合中小于或者等于给定元素的最大元素,如果不存在这样的元素,返回null.
- ceiling(E e) 方法返回在这个集合中大于或者等于给定元素的最小元素,如果不存在这样的元素,返回null.
public static int maxSumSubmatrix(int[][] matrix, int k) {
int ans = Integer.MIN_VALUE;
for (int i = 0; i < matrix.length; i++) {
int[] preSum = new int[matrix[0].length];
for (int j = i; j < matrix.length; j++) {
for (int q = 0; q < matrix[0].length; q++) {
preSum[q] = preSum[q] + matrix[j][q];
}
ans = Math.max(ans, maxSumArea(preSum, k));
}
}
return ans;
}
/*
* floor(E e) 方法返回在这个集合中小于或者等于给定元素的最大元素,如果不存在这样的元素,返回null.
* ceiling(E e) 方法返回在这个集合中大于或者等于给定元素的最小元素,如果不存在这样的元素,返回null.
* */
//一维数组中不超过k的最大子序列的和
//这里最重要的就是使用treeSet可以将问题的复杂度降为logn
//ceiling()函数太牛逼了,就是找到treeSet中大于或者等于给定元素的最小元素,如果不存在这样的元素,返回null
public static int maxSumArea(int[] nums, int target) {
int ans = Integer.MIN_VALUE;
TreeSet<Integer> set = new TreeSet<>();
int preSum = 0;
set.add(0);
for (int i : nums) {
preSum += i;
Integer ceiling = set.ceiling(preSum - target);//找到大于或者等于给定元素的最小元素,如果不存在这样的元素,返回null
if (ceiling != null) {
//preSum-ceiling是这一轮的最大和
ans = Math.max(ans, preSum - ceiling);
}
set.add(preSum);
}
return ans;
}