给一个大小为 m x n 的矩阵 arr 和一个整数阈值 threshold。
请你返回元素总和小于或等于阈值的正方形区域的最大边长;如果没有这样的正方形区域,则返回 0 。
示例 1:
输入:arr = [[1,1,3,2,4,3,2],[1,1,3,2,4,3,2],[1,1,3,2,4,3,2]], threshold = 4
输出:2
----------------------->x
|1,1,3,2,4,3,2
|1,1,3,2,4,3,2
|1,1,3,2,4,3,2
|
|
y
解释:总和小于 4 的正方形的最大边长为 2。
示例 2:
输入:arr = [[2,2,2,2,2],[2,2,2,2,2],[2,2,2,2,2],[2,2,2,2,2],[2,2,2,2,2]], threshold = 1
输出:0
示例 3:
输入:arr = [[1,1,1,1],[1,0,0,0],[1,0,0,0],[1,0,0,0]], threshold = 6
输出:3
示例 4:
输入:arr = [[18,70],[61,1],[25,85],[14,40],[11,96],[97,96],[63,45]], threshold = 40184
输出:2
提示:
1 <= m, n <= 300
m == mat.length
n == mat[i].length
0 <= mat[i][j] <= 10000
0 <= threshold <= 10^5
遍历所有可能的正方形区域:
1、考虑正方形的边长从 1 到 Min(M,N)(M 为矩阵长度,N 为矩阵宽度)
2、考虑正方形右下角的坐标从 (0, 0) 到 (M, N)
3、判断正方形是否存在(可能会超出边界,通过左上角坐标判断),如果存在则验证该正方形区域的元素总和。
计算出二维前缀和 dp[i][j]:从 (0, 0) 到 (i, j) 内元素的总和。
已知 dp[i][j] 必定包含一个元素 arr[i][j],假设我们已经计算出部分前缀和 dp[x][y](x < i 且 y < j),那么 dp[i][j] = arr[i][j] + dp[i - 1][j] + dp[i][j - 1] - dp[i - 1][j - 1] (arr[i][j]注意下标变化)
package com.loo;
public class MaxSideLength {
public static void main(String[] args) {
int[][] arr = new int[][] {
{1,1,3,2,4,3,2},
{1,1,3,2,4,3,2},
{1,1,3,2,4,3,2}
};
int[][] arr2 = new int[][] {
{2,2,2,2,2},
{2,2,2,2,2},
{2,2,2,2,2},
{2,2,2,2,2},
{2,2,2,2,2}
};
int[][] arr3 = new int[][] {
{1,1,1,1},
{1,0,0,0},
{1,0,0,0},
{1,0,0,0}
};
int[][] arr4 = new int[][] {
{18,70},
{61,1},
{25,85},
{14,40},
{11,96},
{97,96},
{63,45}
};
int k = 4;
System.out.println(getMaxSideLength(arr , k));
System.out.println(getMaxSideLength2(arr , k));
}
public static int getMaxSideLength(int[][] arr , int threshold) {
if (arr == null || arr.length == 0 || arr[0].length == 0) {
return 0;
}
int result = 0;
int m = arr.length;
int n = arr[0].length;
int[][] dp = new int[m+1][n+1];
for (int i=1;i<=m;i++) {
for (int j=1;j<=n;j++) {
// arr 坐标从 0 开始
dp[i][j] = arr[i-1][j-1] + dp[i-1][j] + dp[i][j-1] - dp[i-1][j-1];
}
}
for (int k=1;k<=Math.min(m, n);k++) {
for (int i=1;i<=m;i++) {
for (int j=1;j<=n;j++) {
if (i-k<0 || j-k<0) {
continue;
}
int temp = dp[i][j] - dp[i-k][j] - dp[i][j-k] + dp[i-k][j-k];
if (temp <= threshold) {
result = Math.max(result, k);
}
}
}
}
return result;
}
public static int getMaxSideLength2(int[][] arr , int threshold) {
if (arr == null || arr.length == 0 || arr[0].length == 0) {
return 0;
}
int m = arr.length;
int n = arr[0].length;
int[][] dp = new int[m+1][n+1];
for (int i=1;i<=m;i++) {
for (int j=1;j<=n;j++) {
dp[i][j] = arr[i-1][j-1] + dp[i-1][j] + dp[i][j-1] - dp[i-1][j-1];
}
}
int l = 0;
int k = Math.min(m, n);
while (l<=k) {
int mid = l + (k-l)/2;
if (l==k || l+1==k) {
break;
}
if (thresholdHelp(dp , m , n , mid , threshold)) {
l = mid;
} else {
k = mid - 1;
}
}
if (thresholdHelp(dp , m , n , k , threshold)) {
return k;
} else {
return l;
}
}
public static boolean thresholdHelp(int[][] arr , int m , int n , int k , int threshold) {
for (int i=1;i<=m;i++) {
for (int j=1;j<=n;j++) {
if (i-k<0 || j-k<0) {
continue;
}
int temp = arr[i][j] - arr[i-k][j] - arr[i][j-k] + arr[i-k][j-k];
if (temp<=threshold) {
return true;
}
}
}
return false;
}
}