POJ地址:http://poj.org/problem?id=3494
题目的描述很简单,在一个M * N的矩阵中,所有的元素只有0和1, 找出只包含1的最大矩形。
例如:图中是一个4 × 6的矩形,画出红色的是我们要找到的区域。
最开始见过这个题目是在看一个人写的面经里面,当时完全没有感觉,不知道怎么做。后来知道了一个东西叫单调栈然后做了一些题,居然发现POJ上的这个题目,和那个面试题一模一样。
所以就研究明白,分享一下。
这题目如果用暴力做的话,方法是很显然的,对图中的任意点,(i, j), 向它的右下侧找到一个右下端点( r, u), 然后检测方块 (i, j) -> (r, u) 是不是全1. 如果是全1, 我们就找到一个合适的矩形1. 在枚举的同时更新找到最大矩形。
如果这样的话,枚举复杂度是O(M * N), 找下端点 复杂度是 O (M * N), 然后检测的复杂度也是 O(M*N) 最后算法的复杂度是 O(M^3 * N^3)
显然这个复杂度过了点。
仔细观察发现(这个思路也是别人提醒的,感觉自己现在依然不能很准确的建模):
因为我们要找的是矩形,所以它一定是以 某个行元素开始的,如果枚举行的时候,我们会发现:
对于第一行:
对于第二行:
第三行:
第四行:
这样的话,其实我们要找到的某个矩形就转换成 一某一个行开始的 histogram的最大矩形问题了。
那么我们原始矩形可以变成如下的形式的数据:
第一行表示,我们以第一行作为底边,所形成的 histogram的高度,其他行也类似。
所以问题变成了 枚举每一行,然后求出每一行对应的histogram的最大矩形。
关于histogram求最大矩形
可以看看 http://blog.csdn.net/hopeztm/article/details/7868581
因为到了这里以后,基本的原理和histogram是一样的,所以就不再重复了。
代码如下:
Memory: 31676K Time: 1782MS
Language: C++ Result: Accepted
Source Code
#include
#include
#define MAX_LEN 2005
int nRow, nCol;
int matrix[MAX_LEN][MAX_LEN]; //原数据
int heights[MAX_LEN][MAX_LEN];//用这个数组来描述 histogram,其中heights[i]表示 以第i行走底的histogram,里面的元素表示对应列的高度
struct Node
{
int height;
int position;
Node(int _height, int _from): height(_height), position(_from)
{
}
Node()
{
}
};
int max(int a, int b)
{
return a>b ? a : b;
}
//自己实现堆栈,因为stl的栈太慢了,会超时
int topID;
Node stack[MAX_LEN];
//栈对应的操作
void push(const Node & t)
{
stack[topID++] = t;
}
const Node top()
{
return stack[topID-1];
}
void pop()
{
topID--;
}
int GetArea(int iRow) //用单调栈来枚举其中以 某一行做底的 histogram 所得到的最大矩形面积。
{
topID = 0;
push(Node(-1, 0));
int i;
int area, maxArea = 0;
int position, height;
for( i = 0; i <= nCol; i++)
{
position = i + 1;
if(i == nCol)
{
height = -1;
}
else
{
height = heights[iRow][i];
}
Node t(height, position);
while(top().height > height)
{
t = top();
pop();
area = (position - t.position) * t.height;
if(area > maxArea)
{
maxArea = area;
}
}
push(Node(height, t.position));
}
return maxArea;
}
int main()
{
while(scanf("%d%d", &nRow, &nCol) != EOF)
{
int i,j;
int b;
for( i = 0;i < nRow; i++)
{
for( j = 0; j < nCol; j++)
{
scanf("%d", &matrix[i][j]);
}
}
//求histogram,求的时候,如果以 i 行为底边,j对应的高度是 从i 到 最高连续的1 的数量
memcpy(heights, matrix, sizeof(matrix));
for( i = 0; i < nCol; i++)
{
for( j = 1; j < nRow; j++)
{
if(heights[j][i] != 0)
{
heights[j][i] += heights[j-1][i];
}
}
}
int maxArea = 0, Area;
for( i = 0; i < nRow; i++)
{
Area = GetArea(i);
maxArea = max(maxArea, Area);
}
printf("%d\n", maxArea);
}
}
其中栈是自己实现的,因为stl的栈会超时。