0%

LeetCode-Day20

85. 最大矩形

给定一个仅包含 0 和 1 的二维二进制矩阵,找出只包含 1 的最大矩形,并返回其面积。

示例:

1
2
3
4
5
6
7
8
输入:
[
["1","0","1","0","0"],
["1","0","1","1","1"],
["1","1","1","1","1"],
["1","0","0","1","0"]
]
输出: 6

方法一. 动态规划——使用柱状图的优化暴力算法

算法

我们可以以常数时间计算出在给定的坐标结束的矩形的最大宽度。我们可以通过记录每一行中每一个方块连续的“1”的数量来实现这一点。每遍历完一行,就更新该点的最大可能宽度。通过以下代码即可实现。 row[i] = row[i - 1] + 1 if row[i] == '1'.

img

一旦我们知道了每个点对应的最大宽度,我们就可以在线性时间内计算出以该点为右下角的最大矩形。当我们遍历列时,可知从初始点到当前点矩形的最大宽度,就是我们遇到的每个最大宽度的最小值。
我们定义:

maxWidth=min(maxWidth,widthHere)maxWidth=min(maxWidth,widthHere)

$curArea = maxWidth * (curre***ow - originalRow + 1)

curArea=maxWidth∗(curre∗∗∗ow−originalRow+1)$ $maxArea = max(maxArea, curArea) $$maxArea=max(maxArea,curArea)$ 下面的动画有助于理解。给定每个点的最大宽度,可计算出底端黄色方块的最大矩形面积。 ![img](https://pic.leetcode-cn.com/bb40b26be66a20c49bf797b908fd00589c1df90c27c1e4789323e1d0a983b8e6-image.png) ![img](https://pic.leetcode-cn.com/14a6767d8e49b00e351b6e052143dfa826148c22a7e13c3a05c55ea401d05f18-image.png) ![img](https://pic.leetcode-cn.com/d553e8ea8ba5f36a01dadd3530a31cadc2a98aa5b7d8f591fcb36b6c2604784a-image.png) ![img](https://pic.leetcode-cn.com/31d96446efe7b2f759b5caefb262310b6807219b7f9974ebfb00b6b905a84adb-image.png) ![img](https://pic.leetcode-cn.com/c63ef207b988b004efc6fc7c2755a1653f62f9a09ccb4210169f692fc1c7cebf-image.png) 对每个点重复这一过程,就可以得到全局最大。 注意,我们预计算最大宽度的方法事实上将输入转化成了一系列的柱状图,每一栏是一个新的柱状图。我们在针对每个柱状图计算最大面积。 ![image.png](https://pic.leetcode-cn.com/ffba9c5b4b0799150e5b798a73d96c8313522362e9b5290dcff7d9a43f46ba14-image.png)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int maximalRectangle(vector<vector<char>>& matrix) {
if(matrix.size() == 0) {
return 0;
}
vector<vector<int>> dp(matrix.size(), vector<int>(matrix[0].size(), 0));
int maxArea = 0;
for(int i = 0;i < matrix.size();i++) {
for(int j = 0;j < matrix[0].size();j++) {
if(matrix[i][j] == '0') {
continue;
}
dp[i][j] = j == 0 ? 1 : dp[i][j - 1] + 1;
int width = dp[i][j];
for(int k = i;k >= i;k--) {
width = min(width, dp[k][j]);
maxArea = max(maxArea, width * (i - k + 1));
}
}
}
return maxArea;
}
#### 方法二:动态规划——每个点的最大高度 个人理解: `height`用于储存当前位置列的最大高度,即向上可以延伸多少高度 `left`用于存储当前位置上`max{前面积累的最左边界, 当前行向左扩展的最左边界}` `right`用于存储当前位置上`max{前面积累的最右边界, 当前行向右扩展的最右边界}`
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
int maximalRectangle (vector<vector<char>>& matrix) {
if(matrix.size() == 0) {
return 0;
}
int m = matrix.size(), n = matrix[0].size();
vector<int> left(n, 0);
vector<int> right(n, n);
vector<int> height(n, 0);

int maxArea = 0;
for(int i = 0; i < m; i++) {
int cur_left = 0, cur_right = n;
// update height
for(int j = 0; j < n; j++) {
if(matrix[i][j] == '1') height[j]++;
else height[j] = 0;
}
// update left
for(int j=0; j<n; j++) {
if(matrix[i][j]=='1') left[j]=max(left[j],cur_left);
else {left[j]=0; cur_left=j+1;}
}
// update right
for(int j = n - 1; j >= 0; j--) {
if(matrix[i][j] == '1') right[j] = min(right[j], cur_right);
else {right[j] = n; cur_right = j;}
}
// update area
for(int j = 0; j < n; j++) {
maxArea = max(maxArea, (right[j] - left[j]) * height[j]);
}
}
return maxArea;
}
### 88. 合并两个有序数组 给定两个有序整数数组 nums1 和 nums2,将 nums2 合并到 nums1 中,使得 num1 成为一个有序数组。 说明: 初始化 nums1 和 nums2 的元素数量分别为 m 和 n。 你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。 示例:
1
2
3
4
5
输入:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6], n = 3

输出: [1,2,2,3,5,6]
1
2
3
4
5
6
7
8
9
10
11
12
13
class Solution {
public:
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
int i = m - 1, j = n - 1;
for(int pos = m + n - 1; pos >= 0; pos--) {
if(i >= 0 && (j < 0 || nums1[i] >= nums2[j])) {
nums1[pos] = nums1[i--];
} else {
nums1[pos] = nums2[j--];
}
}
}
};
这里的重点就是**从后往前**填写nums1,而不要从前往后遍历。 ### 89. 格雷编码 格雷编码是一个二进制数字系统,在该系统中,两个连续的数值仅有一个位数的差异。 给定一个代表编码总位数的非负整数 n,打印其格雷编码序列。格雷编码序列必须以 0 开头。 **示例 1:**
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
输入: 2
输出: [0,1,3,2]
解释:
00 - 0
01 - 1
11 - 3
10 - 2

对于给定的 n,其格雷编码序列并不唯一。
例如,[0,2,3,1] 也是一个有效的格雷编码序列。

00 - 0
10 - 2
11 - 3
01 - 1
**示例 2:**
1
2
3
4
5
输入: 0
输出: [0]
解释: 我们定义格雷编码序列必须以 0 开头。
给定编码总位数为 n 的格雷编码序列,其长度为 2n。当 n = 0 时,长度为 20 = 1。
因此,当 n = 0 时,其格雷编码序列为 [0]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
public:
vector<int> grayCode(int n) {
vector<int> ans;
if(n == 0) {
ans.push_back(0);
return ans;
}
ans.push_back(0);
ans.push_back(1);
for(int i = 1;i < n;i++) {
int num = 1 << i;
for(int j = ans.size() - 1;j >= 0;j--) {
ans.push_back(ans[j] + num);
}
}
return ans;
}
};
解释:这一道题也是突然就想到怎么做的了... 在原先就是格雷码的基础之上,从后往前遍历,依次往前面添加一个1,即如下图: 0 `-->` 10 1 `-->` 11 所以第一次得到 00 01 11 10 00 `-->` 100 01 `-->` 101 11 `-->` 111 10 `-->` 110 所以第二次得到 000 001 011 010 110 111 101 100 以此类推,每一轮遍历都从vector的末尾开始,然后向上在最前面加1,在数值上的体现就是加上$2^n$