单调栈解决取矩形问题

单调栈解决取矩形问题

​ 前言:这一类问题不知道是什么问题,emmm大概意思就是从一个混合着可行点和不可行点的矩形中能取出的充满可行点的矩形的数目。这一类问题应该有一个官方的名字,大家如果知道可以在评论区中给出。

一 思想概括

对于这样的一个数据,其中1为可行点,求最多能得到多少个矩形

1 0 1 0
1 1 1 0
1 1 1 1

​ 设 a [ i ] [ j ] a[i][j] a[i][j]点的向上的可拓展的高度为 h h h,那么我们要查找的就是在此高度范围内包含 a [ i ] [ j ] a[i][j] a[i][j]点的长方形数目,因此我们要得到这个高度在左右两边可以拓展到哪里,设在左边第一个高度小于等于 h h h的位置为 l l l,右边第一个高度小于 h h h的位置为r,可以得到方案总数为 ( i − l ) ∗ ( r − i ) ∗ h (i-l)*(r -i)*h (il)(ri)h

​ 之所以两边不一样是因为我们要避免重复的计算,比如在这样一个格局中,如果我们选择两边都是小于,则会出现重复的计算在第一次时计算得到的是 3 ∗ 1 ∗ 3 3 * 1*3 313,第二次是 2 ∗ 2 ∗ 3 2 * 2*3 223,包含所有点的一种以及其他几种被反复运算。

1 1 1
1 1 1
1 1 1

二 单调栈的实现

​ 单调栈的思想就是如果你比别人时间更长并且更菜,那么你就被取代,并且新人只能从新向旧不断挑战,而你会被标记上打败你的人的序号

int index[N];
for(int i = 1;i <= n;i++)
{
   while(!stack_empty&&h[i] < h[stack[top]]) index[stack_top] = i, stack_pop;
   stack_push(i);
}

​ 通过单调栈的使用我们可以得到一个点的左(右)第一个大于(小于)的数字的位置

三 code

#include<iostream>
#define N 1010
using namespace std;
int r[N],l[N],h[N],d[N][N],n,m,k[N],ans;
void dql()
{
    int top = 0;
    for(int i = m;i >= 1;i--)
    {
        while(top&&h[i] <= h[k[top]]) l[k[top]] = i,top--;
        top++;
        k[top] = i;
    }
    while(top) l[k[top]]=0,top--;
    top = 0;
    for(int i = 1;i <= m;i++)
    {
        while(top&&h[i] < h[k[top]]) r[k[top]] = i,top-
        top++;
        k[top] = i;
    }
    while(top) r[k[top]]=m+1,top--;
    for(int i = 1;i <= m;i++)
    {
        ans += h[i]*(i - l[i])*(r[i] - i);
    }
    return;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i = 1;i <= n;i++)
        for(int j = 1;j <= n;j++)
        {
            char temp;
            cin>>temp;
            if(temp=='*') d[i][j] = 1;
        }
    for(int i = 1;i <= n;i++)
    {
        for(int j = 1;j <= m;j++)
        {
            if(d[i][j]) h[j] = 0;
            else h[j]++;
        }
    }
    printf("%d",ans);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值