统计全 1 子矩形
- leetcode.1504. 统计全 1 子矩形
- 题目描述
- 单调栈解题
- 代码演示
- 单调栈专题
leetcode.1504. 统计全 1 子矩形
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/count-submatrices-with-all-ones
题目描述
给你一个 m x n 的二进制矩阵 mat ,请你返回有多少个 子矩形 的元素全部都是 1 。
示例1:
输入:mat = [[1,0,1],[1,1,0],[1,1,0]]
输出:13
解释:
有 6 个 1x1 的矩形。
有 2 个 1x2 的矩形。
有 3 个 2x1 的矩形。
有 1 个 2x2 的矩形。
有 1 个 3x1 的矩形。
矩形数目总共 = 6 + 2 + 3 + 1 + 1 = 13 。
示例2:
输入:mat = [[0,1,1,0],[0,1,1,1],[1,1,1,0]]
输出:24
解释:
有 8 个 1x1 的子矩形。
有 5 个 1x2 的子矩形。
有 2 个 1x3 的子矩形。
有 4 个 2x1 的子矩形。
有 2 个 2x2 的子矩形。
有 2 个 3x1 的子矩形。
有 1 个 3x2 的子矩形。
矩形数目总共 = 8 + 5 + 2 + 4 + 2 + 2 + 1 = 24 。
提示:
1 <= m, n <= 150
mat[i][j] 仅包含 0 或 1
单调栈解题
首先看第一个问题,只有一行的时候,怎么计算矩形的数量呢.
如图:
矩形数量的计算公式为.
Sum(n) = n * (n + 1) / 2;
有了上面的计算公式后,我们在以第一行为底座,去计算每一列矩形的高度.有了高度后,可以按单调栈的方式去计算数量了.
// 比如
// 1
// 1
// 1 1
// 1 1 1
// 1 1 1
// 1 1 1
//
// 2 … 6 … 9
// 如上图,假设在6位置,1的高度为6
// 在6位置的左边,离6位置最近、且小于高度6的位置是2,2位置的高度是3
// 在6位置的右边,离6位置最近、且小于高度6的位置是9,9位置的高度是4
// 此时我们求什么?
// 1) 求在3~8范围上,必须以高度6作为高的矩形,有几个?
// 2) 求在3~8范围上,必须以高度5作为高的矩形,有几个?
// 也就是说,<=4的高度,一律不求
// 那么,1) 求必须以位置6的高度6作为高的矩形,有几个?
// 3…3 3…4 3…5 3…6 3…7 3…8
// 4…4 4…5 4…6 4…7 4…8
// 5…5 5…6 5…7 5…8
// 6…6 6…7 6…8
// 7…7 7…8
// 8…8
// 这么多!= 21 = (9 - 2 - 1) * (9 - 2) / 2
// 这就是任何一个数字从栈里弹出的时候,计算矩形数量的方式
代码演示
/**
* 计算矩形的数量
* @param mat
* @return
*/
public static int numSubmat(int[][] mat) {
if (mat == null || mat.length == 0 || mat[0].length == 0){
return 0;
}
int n = mat.length;
int m = mat[0].length;
int[] ans = new int[m];
int nums = 0;
for (int i = 0; i < n;i++){
for (int j = 0;j < m;j++){
ans[j] = mat[i][j] == 0 ? 0 : ans[j] + 1;
}
nums += countFromBottom(ans);
}
return nums;
}
/**
* 单调栈,用数组来实现栈
* @param heights
* @return
*/
public static int countFromBottom(int[]heights){
//记录计算的结果
int ans = 0;
//用数组来代替栈结构,常数时间会得到优化,速度变快
int[]stack = new int[heights.length];
//来标记栈顶元素,最开始没有值,标记为-1
int stackSize = -1;
for (int i = 0; i < heights.length;i++){
while (stackSize != -1 && heights[stack[stackSize]] >= heights[i]){
int cur = stack[stackSize--];
if (heights[cur] > heights[i]){
int left = stackSize == -1 ? -1 : stack[stackSize];
int maxDown = Math.max(left == -1 ? 0 : heights[left],heights[i]);
int width = i - left - 1;
ans += (heights[cur] - maxDown) * count(width);
}
}
stack[++stackSize] = i;
}
while (stackSize != -1){
int cur = stack[stackSize--];
int left = stackSize == -1 ? -1 : stack[stackSize];
int maxDown = left == -1 ? 0 : heights[left];
int width = heights.length - left - 1;
ans += (heights[cur] - maxDown) * count(width);
}
return ans;
}
/**
* n 是宽度
* 公式 计算
*/
public static int count(int n){
return (n * (n + 1)) >> 1;
}
单调栈专题
单调栈的实现-单调递减栈和单调递增栈
leetcode1856. 子数组最小乘积的最大值
leetcode84. 柱状图中最大的矩形
leetcode.85. 最大矩形
leetcode42. 接雨水
leetcode739. 每日温度