单调栈解决取矩形问题
前言:这一类问题不知道是什么问题,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 (i−l)∗(r−i)∗h
之所以两边不一样是因为我们要避免重复的计算,比如在这样一个格局中,如果我们选择两边都是小于,则会出现重复的计算在第一次时计算得到的是 3 ∗ 1 ∗ 3 3 * 1*3 3∗1∗3,第二次是 2 ∗ 2 ∗ 3 2 * 2*3 2∗2∗3,包含所有点的一种以及其他几种被反复运算。
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);
}