最大红矩形
问题描述
- 有一个 n ∗ m n*m n∗m 的棋盘,棋盘上的每个点都是红的或绿的
- 你需要找出一个面积最大的矩形区域,使得其中没有绿的格子
输入格式
- 第一行 2 个正整数 n,m,描述棋盘尺寸
- 接下来 n 行描述这个棋盘,每行 m 个字符,每个字符为 . 或 X,其中 . 表示这个位置是红色的,X 表示这个位置是绿色的
输出格式
- 一行一个整数,表示最大面积。
数据范围
- 对于 30% 的数据,n,m<=100
- 对于 60% 的数据,n,m<=400
- 对于 100% 的数据,n,m<=1,000
关键算法实现
总体分析
- 求
n
∗
m
n*m
n∗m的最大红矩形面积可以转换为:
- 求1*m的直方图的面积
- 再求n遍不同高度的直方图的面积
- 关键是如何在每一行都讲求以便最大直方图面积
- 从第1行枚举到第n行, 且用 h i h_i hi表示第i列的高度,那么,当从第k-1行到k行的时候,有: h i = { h i + 1 c o l o r k , i = R e d 0 c o l o r k , i = G r e e n h_i = \left \{ \begin{array}{cccc} h_i + 1 & color_{k,i} = Red \\ 0 & color_{k,i} = Green \end{array} \right. hi={hi+10colork,i=Redcolork,i=Green
- 高度得到之后,直接套用最大直方图的算法即可!
1 ) 算法1:时间复杂度为: O ( n 4 ) O(n^4) O(n4)的实现
// n层最大直方图, 蛮力做法
// n, m:意义如题
// matrix:一个字符串数组,用来记录所给矩形,matrix[i][j]表示所给矩形第i行第j列的字符(下标均从0开始)
// 返回值:题目所求答案,即最大矩形面积
int getResult(int n, int m, string *matrix) {
/* 请在这里设计你的算法,并将答案返回 */
int ans = 0; // 记录答案
int *height = new int[m + 2](); // 存放高度的数组, 是一个长度空间为:m+2的数组, 左右增加两个高度为0的哨兵
stack<int> myStack; // 单调栈,记录的是在height数组中的位置(索引),栈顶元素所对应的高度是最高的。
// 算法开始,进行每一行的遍历,k表示当前所枚举的是哪一行
for (int k = 0; k < n; ++k) {
// 1. 对当前行的每一列进行遍历,更新当前行和当前列的高度
for (int i = 1; i<=m; ++i) {
// 注意遍历的i是从[1,m], 更新的i要做-1操作,索引从0开始
if(matrix[k][i-1] == '.') {
// height[i] = height[i] + 1; // 如果匹配到了红色,那么高度自增1
// height[i] += 1;
height[i]++;
}
else {
// 匹配到绿色,那么高度降为0
height[i] = 0;
}
}
// 对每行存放的索引栈进行重置操作
while (!myStack.empty()) {
myStack.pop();
}
// 初始化栈,压入0,表示对卡位点占位,因为height[0] = 0,
myStack.push(0);
// 第一层循环枚举直方图左边界位置
for (int a = 1; a <= m; ++a) {
// 第二层循环枚举直方图右边界位置
for (int b = a; b <= m; ++b) {
// 要求,保证所有 h[i] 不超过 32767,记录最小高度, 这里只做数据的初始化,取一个比最大值大的值即可
int minH = 50000;
// 从a到b更新最小高度,这里的高要求是最矮的,木桶效应
for (int c = a; c <= b; ++c) {
minH = min(minH, height[c]); // 更新最小高度
}
ans = max(ans, (b - a + 1) * minH); // 更新最大面积
}
}
}
delete[] height;
return ans;
}
2 ) 算法2:时间复杂度为: O ( n 3 ) O(n^3) O(n3)的实现卡位
// n层最大直方图, 卡位做法
// n, m:意义如题
// matrix:一个字符串数组,用来记录所给矩形,matrix[i][j]表示所给矩形第i行第j列的字符(下标均从0开始)
// 返回值:题目所求答案,即最大矩形面积
int getResult(int n, int m, string *matrix) {
/* 请在这里设计你的算法,并将答案返回 */
int ans = 0; // 记录答案
int *height = new int[m + 2](); // 存放高度的数组, 是一个长度空间为:m+2的数组, 左右增加两个高度为0的哨兵
stack<int> myStack; // 单调栈,记录的是在height数组中的位置(索引),栈顶元素所对应的高度是最高的。
// 算法开始,进行每一行的遍历,k表示当前所枚举的是哪一行
for (int k = 0; k < n; ++k) {
// 1. 对当前行的每一列进行遍历,更新当前行和当前列的高度
for (int i = 1; i<=m; ++i) {
// 注意遍历的i是从[1,m], 更新的i要做-1操作,索引从0开始
if(matrix[k][i-1] == '.') {
height[i]++; // 如果匹配到了红色,那么高度自增1
}
else {
// 匹配到绿色,那么高度降为0
height[i] = 0;
}
}
// 对每行存放的索引栈进行重置操作
while (!myStack.empty()) {
myStack.pop();
}
// 初始化栈,压入0,表示对卡位点占位,因为height[0] = 0,
myStack.push(0);
// 第一层循环枚举直方图所有的列, 从1~m, 表示第1列到第m列
for (int a = 1; a <= m; ++a) {
// 取出当前列的高度
int curH = height[a];
// 左卡位点
int lo = 0;
// 右卡位点
int hi = 0;
// 第二层找到当前列的左右卡位点,并计算最大矩形面积
// 找到左卡位点
for (lo = a - 1; lo >= 1; lo --){
if(height[lo] < curH) {
break;
}
}
// 找到右卡位点
for (hi = a + 1; lo <= n; hi++){
if(height[hi] < curH) {
break;
}
}
// 计算当前最大面积
ans = max(ans, (hi - lo - 1) * curH);
}
}
delete[] height;
return ans;
}
3 ) 算法3:时间复杂度为: O ( n 2 ) O(n^2) O(n2)的实现:单调栈
// n层最大直方图, 单调栈做法
// n, m:意义如题
// matrix:一个字符串数组,用来记录所给矩形,matrix[i][j]表示所给矩形第i行第j列的字符(下标均从0开始)
// 返回值:题目所求答案,即最大矩形面积
int getResult(int n, int m, string *matrix) {
/* 请在这里设计你的算法,并将答案返回 */
int ans = 0; // 记录答案
int *height = new int[m + 2](); // 存放高度的数组, 是一个长度空间为:m+2的数组, 左右增加两个高度为0的哨兵
stack<int> myStack; // 单调栈,记录的是在height数组中的位置(索引),栈顶元素所对应的高度是最高的。
// 算法开始,进行每一行的遍历,k表示当前所枚举的是哪一行
for (int k = 0; k < n; ++k) {
// 1. 对当前行的每一列进行遍历,更新当前行和当前列的高度
for (int i = 1; i<=m; ++i) {
// 注意遍历的i是从[1,m], 更新的i要做-1操作,索引从0开始
if(matrix[k][i-1] == '.') {
height[i]++; // 如果匹配到了红色,那么高度自增1
}
else {
// 匹配到绿色,那么高度降为0
height[i] = 0;
}
}
// 对每行存放的索引栈进行重置操作
while (!myStack.empty()) {
myStack.pop();
}
// 初始化栈,压入0,表示对卡位点占位,因为height[0] = 0,
myStack.push(0);
// 单调栈算法,循环到m+1, 因为height[m+1] = 0;
for (int i = 1; i <= m + 1; ++i) {
while(height[myStack.top()] > height[i]) {
int nowHeight = height[myStack.top()];
myStack.pop();
ans = max(ans, (i - myStack.top() - 1) * nowHeight);
}
myStack.push(i);
}
}
delete[] height;
return ans;
}
通用的输入程序
int main() {
ios::sync_with_stdio(false); // 读入优化
int n, m;
cin >> n >> m;
// 输入字符形矩阵 数组
string *matrix = new string[n]();
// 连续输入n行矩阵
for (int i = 0; i < n; ++i)
cin >> matrix[i];
cout << getResult(n, m, matrix) << endl;
delete[] matrix;
return 0;
}
测试输入数据
- 输入:
4 5 ..... XXXXX .X... .....
- 输出:6