例题:
Largest Rectangle in a Histogram
题意:找出柱状图内最大矩形块,求最大面积
解析:
用单调栈处理,找不小于该点的最左端和最右端
ans=max(ans,(r-l+1)*h[i]);
ac:
#include<bits/stdc++.h>
#define ll long long
#define MAXN 100005
using namespace std;
stack<int> sk;
int a[MAXN];
int aa[MAXN];
int bb[MAXN];
int main()
{
int n,x,y;
while(scanf("%d",&n)&&n)
{
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
{
while(sk.size()&&a[sk.top()]>=a[i])
sk.pop();
if(sk.empty())
aa[i]=1;
else
aa[i]=sk.top()+1;//找相等的点,即是第一个小于的点+1
sk.push(i);
}
while(!sk.empty())
sk.pop();
for(int i=n;i>=1;i--)
{
while(sk.size()&&a[sk.top()]>=a[i])
sk.pop();
if(sk.empty())
bb[i]=n;
else
bb[i]=sk.top()-1;//同上
sk.push(i);
}
while(!sk.empty())
sk.pop();
ll ans=0;//注意int溢出
for(int i=1;i<=n;i++)
ans=max(ans,(ll)(bb[i]-aa[i]+1)*a[i]);
printf("%lld\n",ans);
}
return 0;
}
https://ac.nowcoder.com/acm/contest/881/A
题意:
给定2个数组,1<=l<=r<=m,找到最大的m,对于a数组和b数组在区间(1,m)中的任意(l,r)区间内的最小值下标相同
解析:
直接从左往右遍历,找m点往左的第一个比m小的点,如果a数组和b数组相同ans++,否则跳出
这里单调栈虽然仅仅找的是第一个比a[m]小的,但是最小是可以通过若干次传递的,我们是从左往右,所以满足单调传递性
L[a]=0,L[b]=a,L[c]=b,L[d]=c;=>区间(1,d)中最小的值坐标为a.
m前面的都是相等的,所以可以传递
ac:
#include<bits/stdc++.h>
#define ll long long
#define MAXN 100005
using namespace std;
int a[MAXN],b[MAXN];
int l[MAXN];
int L[MAXN];
void solve(int n)
{
stack<int> sk;
for(int i=1;i<=n;i++)
{
while(sk.size()&&a[sk.top()]>=a[i])
sk.pop();
if(sk.empty())
l[i]=0;
else
l[i]=sk.top();
sk.push(i);
}
while(!sk.empty())
sk.pop();
for(int i=1;i<=n;i++)
{
while(sk.size()&&b[sk.top()]>=b[i])
sk.pop();
if(sk.empty())
L[i]=0;
else
L[i]=sk.top();
sk.push(i);
}
}
int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
scanf("%d",&b[i]);
solve(n);
int f=0;
for(int i=1;i<=n;i++)
{
if(l[i]!=L[i])
break;
f++;
}
cout<<f<<endl;
}
return 0;
}
1.Max answer(单调栈,rmq)
https://nanti.jisuanke.com/t/38228
题意:
求最大的 区间和*区间最小值.(区间内有负数)
解析:
我们把整数部分和负数部分分开求
整数部分:只能是全为整数的区间,就是求矩形最大面积,经典单调栈题目
负数部分:遍历每一个负数a[i]
将区间扩至最大(扩至和最小),然后sum*a[i],贪心一下
扩区间方法:
求出前缀和,后缀和,然后查询a[i]的左边最小的,减去a[i]~a[n],比较是否大于0,判断是否需要往左边扩
查询最小值用rmq算法
总复杂度是O(nlogn)
ac:
线段树+单调栈:170ms
#include<bits/stdc++.h>
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1
#define ll long long
#define MAXN 500005
using namespace std;
int a[MAXN],b[MAXN],c[MAXN];
ll sum[MAXN],sum2[MAXN];
int n;
struct node
{
ll mins[MAXN<<2];
void pushup(int rt)
{
mins[rt]=min(mins[rt<<1],mins[rt<<1|1]);
}
void build(int l,int r,int rt,ll v[])
{
if(l==r)
{
mins[rt]=v[l];
return ;
}
int m=(l+r)>>1;
build(ls,v);
build(rs,v);
pushup(rt);
}
ll query(int L,int R,int l,int r,int rt)
{
if(L<=l&&r<=R){
return mins[rt];
}
int m=(l+r)>>1;
ll res=1e18;
if(L<=m)
res=min(res,query(L,R,ls));
if(R>m)
res=min(res,query(L,R,rs));
return res;
}
}AA,BB;
ll solve()
{
stack<int> sk;
for(int i=1;i<=n;i++)
{
while(sk.size()&&a[sk.top()]>=a[i])
sk.pop();
if(sk.empty())//左边没有,呢么就是0
b[i]=0;
else
b[i]=sk.top();
sk.push(i);
}
while(sk.size())
sk.pop();
for(int i=n;i>=1;i--)
{
while(sk.size()&&a[sk.top()]>=a[i])
sk.pop();
if(sk.empty())
c[i]=n;
else
c[i]=sk.top()-1;
sk.push(i);
}
ll ans=0;
for(int i=1;i<=n;i++)
ans=max(ans,(ll)a[i]*(sum[c[i]]-sum[b[i]]));
return ans;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]),sum[i]=sum[i-1]+a[i];
for(int i=n;i>=1;i--)
sum2[i]=sum2[i+1]+a[i];
ll ans=solve();
AA.build(1,n,1,sum);
BB.build(1,n,1,sum2);
for(int i=1;i<=n;i++)
{
if(a[i]>=0)
continue;
ll aa=AA.query(i,n,1,n,1)-sum[i];
ll bb=BB.query(1,i,1,n,1)-sum2[i];
ans=max(ans,(ll)a[i]*(aa+bb+a[i]));
}
printf("%lld\n",ans);
return 0;
}
rmq+单调栈,rmq常数太大:600ms
#include<bits/stdc++.h>
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1
#define ll long long
#define MAXN 500005
using namespace std;
int a[MAXN],b[MAXN],c[MAXN];
ll sum[MAXN],sum2[MAXN];
int n;
ll solve()
{
stack<int> sk;
for(int i=1;i<=n;i++)
{
while(sk.size()&&a[sk.top()]>=a[i])
sk.pop();
if(sk.empty())//左边没有,呢么就是0
b[i]=0;
else
b[i]=sk.top();
sk.push(i);
}
while(sk.size())
sk.pop();
for(int i=n;i>=1;i--)
{
while(sk.size()&&a[sk.top()]>=a[i])
sk.pop();
if(sk.empty())
c[i]=n;
else
c[i]=sk.top()-1;
sk.push(i);
}
ll ans=0;
for(int i=1;i<=n;i++)
ans=max(ans,(ll)a[i]*(sum[c[i]]-sum[b[i]]));
return ans;
}
struct node
{
ll minn[MAXN][25];
ll query(int a,int b)
{
int k = log2(b-a+1);
return min(minn[a][k],minn[b-(1<<k)+1][k]);
}
void buildrmq(ll n,ll c[])
{
int tem = (ll)floor(log2((double)n));
for(int i=1;i<=n;i++)
minn[i][0]=c[i];
for(int j=1;j<=tem;j++)
{
for(int i=1;i+(1<<j)-1<=n;i++)
minn[i][j] = min(minn[i][j-1],minn[i+(1<<(j-1))][j-1]);
}
}
}AA,BB;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]),sum[i]=sum[i-1]+a[i];
for(int i=n;i>=1;i--)
sum2[i]=sum2[i+1]+a[i];
ll ans=solve();
AA.buildrmq(n,sum);
BB.buildrmq(n,sum2);
for(int i=1;i<=n;i++)
{
if(a[i]>=0)
continue;
ll aa=AA.query(i,n)-sum[i];
ll bb=BB.query(1,i)-sum2[i];
ans=max(ans,(ll)a[i]*(aa+bb+a[i]));
}
printf("%lld\n",ans);
return 0;
}
/*
6
1 -1 -4 3 -5 -4
*/
给定1个01矩阵,求该矩阵中的独立矩阵,独立矩阵为不被其他1个矩阵完全包含
解析:
对于每个点(i,j),求出它往上的连续的1,即高为hh[i],求i点往两边扩展,高度不小于hh[i]的最左和最右(用前缀+单调栈处理)
得到以(L[i],R[i])为底,高为hh[i]的全1矩形
判断该底下面是否是否全为1,全为1就可以继续向下扩展,这个底就不算(用前缀处理)
如果这个底算,呢么给它标记vis[L[i]][R[i]]=i,以同一底的矩阵只能存在一个,去重
ac:
#include<bits/stdc++.h>
#define ll long long
#define MAXN 3005
using namespace std;
char mm[MAXN][MAXN];
int hh[MAXN][MAXN];
int n,m;
int stk[MAXN],top;
int L[MAXN],R[MAXN];
int num[MAXN];
int vis[MAXN][MAXN];
ll ans;
void solve(int x)
{
top=0;stk[++top]=0;
for(int i=1;i<=m;i++)//最左
{
while(top>0&&hh[x][stk[top]]>=hh[x][i])
top--;
L[i]=stk[top]+1;
stk[++top]=i;
}
top=0,stk[++top]=m+1;
for(int i=m;i>=1;i--)//最右
{
while(top>0&&hh[x][stk[top]]>=hh[x][i])
top--;
R[i]=stk[top]-1;
stk[++top]=i;
}
for(int i=1;i<=m;i++)//计算下一层的前缀和
num[i]=num[i-1]+(mm[x+1][i]-'0');
for(int i=1;i<=m;i++)
{
if(mm[x][i]=='0')
continue;
if(vis[L[i]][R[i]]==x)
continue;
if(num[R[i]]-num[L[i]-1]==R[i]-L[i]+1)
continue;
ans++;
vis[L[i]][R[i]]=x;
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%s",mm[i]+1);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(mm[i][j]=='0')
hh[i][j]=0;
else
hh[i][j]=hh[i-1][j]+1;
}
}
ans=0;
for(int i=1;i<=n;i++)
solve(i);
printf("%lld\n",ans);
return 0;
}