经典面试题--寻找01矩阵中最大的1矩形(POJ 3494)

39 篇文章 0 订阅
22 篇文章 0 订阅

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 <stdio.h>
#include <memory.h>


#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的栈会超时。


  • 5
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值