最大矩形面积——悬线法和单调栈

悬线法

悬线法用于求一个矩阵内最大的矩形面积,时空复杂度为 O(n * m)。

所谓悬线法,是说想象一根线悬挂在矩阵上边界或障碍处,保持铅垂,左右移动,然后用这根悬线所能扫到的最大矩形去更新答案。

维护每个点在同一行内向左右能延伸到的最远坐标。记录在 l[i][j]r[i][j] 里。

然后维护每个点向上能延伸到的最靠上的位置,挂悬线,记录在 up[i][j] 。悬线往上挂时要更新当前点的 l[i][j]r[i][j] 值以确保矩形的形状。

例题 P4147 玉蟾宫

代码

#include<set>
#include<map>
#include<queue>
#include<cmath>
#include<ctime>
#include<vector>
#include<cstdio>
#include<string>
#include<cstring>
#include<cstdlib>
#include<iomanip>
#include<iostream>
#include<algorithm>
#include<functional>
#define ll long long
using namespace std;

#ifndef DONLINE_JUDGE
char xch, xB[1 << 15], *xS = xB, *xTT = xB;
#define getc() (xS == xTT && (xTT = (xS = xB) + fread(xB, 1, 1 << 15, stdin), xS == xTT) ? 0 : *xS++)
#endif

#ifndef DBUFFETT
#define getc() getchar()
#endif

int rd(){
	int res = 0, fl = 1;
	char c = getchar();
	while(!isdigit(c)){
		if(c == '-') fl = -1;
		c = getchar();
	}
	while(isdigit(c)){
		res = (res << 3) + (res << 1) + c - '0';
		c = getchar();
	}
	return res * fl;
}
int rdchar(){
    char c = getchar();
    while(!isalpha(c)){
        c = getchar();
    }
    if(c == 'F')    return 1;
    else if(c == 'R')   return 0;
}
int n, m, fld[1010][1010], l[1010][1010], r[1010][1010], up[1010][1010], ans; 
int main(){
    n = rd(); m = rd();
    for(int i = 1; i <= n; ++i){
        for(int j = 1; j <= m; ++j){
            fld[i][j] = rdchar();
        }
    }
    for(int i = 1; i <= n; ++i){
        for(int j = 1; j <= m; ++j){
            if(fld[i][j]){
                l[i][j] = j;
                if(fld[i][j - 1])   l[i][j] = l[i][j - 1]; 
            }
        }
        for(int j = m; j >= 1; --j){
            if(fld[i][j]){
                r[i][j] = j;
                if(fld[i][j + 1])   r[i][j] = r[i][j + 1];
            }
        }
    }
    for(int i = 1; i <= n; ++i){
        for(int j = 1; j <= m; ++j){
            if(fld[i][j]){
                up[i][j] = 1;
                if(fld[i - 1][j]){
                    l[i][j] = max(l[i][j], l[i - 1][j]);
                    r[i][j] = min(r[i][j], r[i - 1][j]);
                    up[i][j] += up[i - 1][j];
                }
            }
            ans = max(ans, (r[i][j] - l[i][j] + 1) * up[i][j]);
        }
    }
    printf("%d\n", ans * 3);
	return 0;
}


单调栈

用于维护下底边在同一高度的矩形。

维护栈内矩形高度递增。

精髓操作是替换矩形以排除无用的矩形边角料。

(见李煜东

借助单调性处理问题的思想在于及时排除不可能的选项,保持策略集合的高度有效性和秩序性,从而为我们做出决策提供更多的条件和方法。

例题 P4147 玉蟾宫

这个题首先要枚举行,然后每一行都做一次单调栈。应该还能优化。

代码

#include<set>
#include<map>
#include<queue>
#include<cmath>
#include<ctime>
#include<vector>
#include<cstdio>
#include<string>
#include<cstring>
#include<cstdlib>
#include<iomanip>
#include<iostream>
#include<algorithm>
#include<functional>
#define ll long long
using namespace std;

#ifndef DONLINE_JUDGE
char xch, xB[1 << 15], *xS = xB, *xTT = xB;
#define getc() (xS == xTT && (xTT = (xS = xB) + fread(xB, 1, 1 << 15, stdin), xS == xTT) ? 0 : *xS++)
#endif

#ifndef DBUFFETT
#define getc() getchar()
#endif

int rd(){
	int res = 0, fl = 1;
	char c = getchar();
	while(!isdigit(c)){
		if(c == '-') fl = -1;
		c = getchar();
	}
	while(isdigit(c)){
		res = (res << 3) + (res << 1) + c - '0';
		c = getchar();
	}
	return res * fl;
}
int rdchar(){
    char c = getchar();
    while(!isalpha(c)){
        c = getchar();
    }
    if(c == 'F')    return 1;
    else if(c == 'R')   return 0;
}
int n, m, fld[1010][1010], ans;
int stk[1010], top, height[1010], w[1010]; 
int main(){
//    freopen("P4147_7.in", "r", stdin);
    n = rd(); m = rd();
    for(int i = 1; i <= n; ++i){
        for(int j = 1; j <= m; ++j){
            fld[i][j] = rdchar();
        }
    }
    for(int i = 1; i <= n; ++i){
        for(int j = 1; j <= m; ++j){
            if(fld[i][j]){
                height[j] ++;
            }
            else height[j] = 0;
        }
        top = 0;
        for(int j = 1; j <= m + 1; ++j){
            int width = 0;
            while(top > 0 && stk[top] >= height[j]){
                width += w[top];
                ans = max(ans, width * stk[top--]);
            }
            stk[++top] = height[j];
            w[top] = width + 1;
        }
        
    }
    printf("%d\n", ans * 3);
	return 0;
}


posted @ 2021-07-05 21:11  咕咕坤  阅读(165)  评论(0编辑  收藏  举报