### 拼图：找到最大的矩形（最大矩形问题）

``````....................
..............######
##..................
.................###
.................###
#####...............
#####...............
#####...............
``````

``````....................
..............######
##...++++++++++++...
.....++++++++++++###
.....++++++++++++###
#####++++++++++++...
#####++++++++++++...
#####++++++++++++...
``````

Shog9 写道：

#### 8 个答案:

``````#include <assert.h>
#include <stdio.h>
#include <stdlib.h>

typedef struct {
int one;
int two;
} Pair;

Pair best_ll = { 0, 0 };
Pair best_ur = { -1, -1 };
int best_area = 0;

int *c; /* Cache */
Pair *s; /* Stack */
int top = 0; /* Top of stack */

void push(int a, int b) {
s[top].one = a;
s[top].two = b;
++top;
}

void pop(int *a, int *b) {
--top;
*a = s[top].one;
*b = s[top].two;
}

int M, N; /* Dimension of input; M is length of a row. */

void update_cache() {
int m;
char b;
for (m = 0; m!=M; ++m) {
scanf(" %c", &b);
fprintf(stderr, " %c", b);
if (b=='0') {
c[m] = 0;
} else { ++c[m]; }
}
fprintf(stderr, "\n");
}

int main() {
int m, n;
scanf("%d %d", &M, &N);
fprintf(stderr, "Reading %dx%d array (1 row == %d elements)\n", M, N, M);
c = (int*)malloc((M+1)*sizeof(int));
s = (Pair*)malloc((M+1)*sizeof(Pair));
for (m = 0; m!=M+1; ++m) { c[m] = s[m].one = s[m].two = 0; }
/* Main algorithm: */
for (n = 0; n!=N; ++n) {
int open_width = 0;
update_cache();
for (m = 0; m!=M+1; ++m) {
if (c[m]>open_width) { /* Open new rectangle? */
push(m, open_width);
open_width = c[m];
} else /* "else" optional here */
if (c[m]<open_width) { /* Close rectangle(s)? */
int m0, w0, area;
do {
pop(&m0, &w0);
area = open_width*(m-m0);
if (area>best_area) {
best_area = area;
best_ll.one = m0; best_ll.two = n;
best_ur.one = m-1; best_ur.two = n-open_width+1;
}
open_width = w0;
} while (c[m]<open_width);
open_width = c[m];
if (open_width!=0) {
push(m0, w0);
}
}
}
}
fprintf(stderr, "The maximal rectangle has area %d.\n", best_area);
fprintf(stderr, "Location: [col=%d, row=%d] to [col=%d, row=%d]\n",
best_ll.one+1, best_ll.two+1, best_ur.one+1, best_ur.two+1);
return 0;
}
``````

``````16 12
0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 1 1 0 0 0 0 0 0 0 0 1 0 0 0
0 0 0 1 1 0 0 1 0 0 0 1 1 0 1 0
0 0 0 1 1 0 1 1 1 0 1 1 1 0 1 0
0 0 0 0 1 1 * * * * * * 0 0 1 0
0 0 0 0 0 0 * * * * * * 0 0 1 0
0 0 0 0 0 0 1 1 0 1 1 1 1 1 1 0
0 0 1 0 0 0 0 1 0 0 1 1 1 0 1 0
0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0
0 0 0 0 1 1 0 0 0 0 1 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0
``````

``````The maximal rectangle has area 12.
Location: [col=7, row=6] to [col=12, row=5]
``````

@lassevk

@lassevk

``````    // 4. Outer double-for-loop to consider all possible positions
//    for topleft corner.
for (int i=0; i < M; i++) {
for (int j=0; j < N; j++) {

// 2.1 With (i,j) as topleft, consider all possible bottom-right corners.

for (int a=i; a < M; a++) {
for (int b=j; b < N; b++) {
``````

HAHA ...... O（m2 n2）..这可能是我想出的。

``````package com.test;

import java.util.Stack;

public class Test {

public static void main(String[] args) {
boolean[][] test2 = new boolean[][] { new boolean[] { false, true, true, false },
new boolean[] { false, true, true, false }, new boolean[] { false, true, true, false },
new boolean[] { false, true, false, false } };
solution(test2);
}

private static class Point {
public Point(int x, int y) {
this.x = x;
this.y = y;
}

public int x;
public int y;
}

public static int[] updateCache(int[] cache, boolean[] matrixRow, int MaxX) {
for (int m = 0; m < MaxX; m++) {
if (!matrixRow[m]) {
cache[m] = 0;
} else {
cache[m]++;
}
}
return cache;
}

public static void solution(boolean[][] matrix) {
Point best_ll = new Point(0, 0);
Point best_ur = new Point(-1, -1);
int best_area = 0;

final int MaxX = matrix[0].length;
final int MaxY = matrix.length;

Stack<Point> stack = new Stack<Point>();
int[] cache = new int[MaxX + 1];

for (int m = 0; m != MaxX + 1; m++) {
cache[m] = 0;
}

for (int n = 0; n != MaxY; n++) {
int openWidth = 0;
cache = updateCache(cache, matrix[n], MaxX);
for (int m = 0; m != MaxX + 1; m++) {
if (cache[m] > openWidth) {
stack.push(new Point(m, openWidth));
openWidth = cache[m];
} else if (cache[m] < openWidth) {
int area;
Point p;
do {
p = stack.pop();
area = openWidth * (m - p.x);
if (area > best_area) {
best_area = area;
best_ll.x = p.x;
best_ll.y = n;
best_ur.x = m - 1;
best_ur.y = n - openWidth + 1;
}
openWidth = p.y;
} while (cache[m] < openWidth);
openWidth = cache[m];
if (openWidth != 0) {
stack.push(p);
}
}
}
}

System.out.printf("The maximal rectangle has area %d.\n", best_area);
System.out.printf("Location: [col=%d, row=%d] to [col=%d, row=%d]\n", best_ll.x + 1, best_ll.y + 1,
best_ur.x + 1, best_ur.y + 1);
}

}
``````

``````[33, 53, 12, 64, 41, 45, 21, 97, 35, 47, 39, 93, 40, 46, 42, 95, 51, 68, 9,
84, 34, 64, 6, 26, 3, 43, 30, 60, 3, 68, 9, 97, 19, 27, 4, 96, 37, 78, 43,
64, 4, 65, 30, 84, 18, 50, 1, 40, 32, 76, 57, 63, 53, 57, 42, 80, 9, 41, 30,
79, 18, 97, 23, 52, 38, 74, 15]

[33, 53, 12, 64, 41, 45, 21, 97, 35, 92, 0, 93, 40, 69, 6, 95, 51, 72, 9, 84, 34, 64, 2, 98, 3, 43, 30, 60, 3, 82, 9, 97, 19, 99, 4, 96, 9, 78, 43, 64, 4, 65, 30, 90, 18, 60, 1, 40, 32, 100, 29, 63, 46, 98, 42, 82, 9, 84, 30, 79, 18, 97, 23, 52, 38, 74]
``````

I am the author of the Maximal Rectangle Solution on LeetCode, which is what this answer is based on.

Since the stack-based solution has already been discussed in the other answers, I would like to present an optimal `O(NM)` dynamic programming solution which originates from user morrischen2008.

Intuition

Imagine an algorithm where for each point we computed a rectangle by doing the following:

• Finding the maximum height of the rectangle by iterating upwards until a filled area is reached

• Finding the maximum width of the rectangle by iterating outwards left and right until a height that doesn't accommodate the maximum height of the rectangle

For example finding the rectangle defined by the yellow point:

We know that the maximal rectangle must be one of the rectangles constructed in this manner (the max rectangle must have a point on its base where the next filled square is height above that point).

For each point we define some variables:

`h` - the height of the rectangle defined by that point

`l` - the left bound of the rectangle defined by that point

`r` - the right bound of the rectangle defined by that point

These three variables uniquely define the rectangle at that point. We can compute the area of this rectangle with `h * (r - l)`. The global maximum of all these areas is our result.

Using dynamic programming, we can use the `h`, `l`, and `r` of each point in the previous row to compute the `h`, `l`, and `r` for every point in the next row in linear time.

Algorithm

Given row `matrix[i]`, we keep track of the `h`, `l`, and `r` of each point in the row by defining three arrays - `height`, `left`, and `right`.

`height[j]` will correspond to the height of `matrix[i][j]`, and so on and so forth with the other arrays.

The question now becomes how to update each array.

`height`

`h` is defined as the number of continuous unfilled spaces in a line from our point. We increment if there is a new space, and set it to zero if the space is filled (we are using '1' to indicate an empty space and '0' as a filled one).

``````new_height[j] = old_height[j] + 1 if row[j] == '1' else 0
``````

`left`:

Consider what causes changes to the left bound of our rectangle. Since all instances of filled spaces occurring in the row above the current one have already been factored into the current version of `left`, the only thing that affects our `left` is if we encounter a filled space in our current row.

As a result we can define:

``````new_left[j] = max(old_left[j], cur_left)
``````

`cur_left` is one greater than rightmost filled space we have encountered. When we "expand" the rectangle to the left, we know it can't expand past that point, otherwise it'll run into the filled space.

`right`:

Here we can reuse our reasoning in `left` and define:

``````new_right[j] = min(old_right[j], cur_right)
``````

`cur_right` is the leftmost occurrence of a filled space we have encountered.

Implementation

``````def maximalRectangle(matrix):
if not matrix: return 0

m = len(matrix)
n = len(matrix[0])

left = [0] * n # initialize left as the leftmost boundary possible
right = [n] * n # initialize right as the rightmost boundary possible
height = [0] * n

maxarea = 0

for i in range(m):

cur_left, cur_right = 0, n
# update height
for j in range(n):
if matrix[i][j] == '1': height[j] += 1
else: height[j] = 0
# update left
for j in range(n):
if matrix[i][j] == '1': left[j] = max(left[j], cur_left)
else:
left[j] = 0
cur_left = j + 1
# update right
for j in range(n-1, -1, -1):
if matrix[i][j] == '1': right[j] = min(right[j], cur_right)
else:
right[j] = n
cur_right = j
# update the area
for j in range(n):
maxarea = max(maxarea, height[j] * (right[j] - left[j]))

return maxarea
``````

``````function maxRectangle(mask) {
var best = {area: 0}
const depth = Array(width).fill(0)
for (var y = 0; y < mask.length; y++) {
const ranges = Array()
for (var x = 0; x < width; x++) {
const d = depth[x] = mask[y][x] ? depth[x] + 1 : 0
if (!ranges.length || ranges[ranges.length - 1].height < d) {
ranges.push({left: x, height: d})
} else {
for (var j = ranges.length - 1; j >= 0 && ranges[j].height >= d; j--) {
const {left, height} = ranges[j]
const area = (x - left) * height
if (area > best.area) {
best = {area, left, top: y + 1 - height, right: x, bottom: y + 1}
}
}
ranges.splice(j+2)
ranges[j+1].height = d
}
}
}
return best;
}

var example = [
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0],
[0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0],
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0],
[0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
[0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
[0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]]

console.log(maxRectangle(example))``````