单调栈
这几天在完成寒假作业的时候,了解到了单调栈和单调队列的使用,以及作用。
单调栈:单调性,强调单调。栈里的元素符合增或减。概念很简单。
刚开始接触单调栈的时候,看别人代码模模糊糊,不是很懂。我写单调栈都是用数组模拟,定义一个s[maxn]数组,s数组就是用来记录栈顶坐标。
下面我们来看看单调栈的基础题。
Bad Hair Day
POJ 3250
这里有三个很基础的题目,适合我们这种刚接触单调栈的初学者。
下面附上自己的AC代码
/* POJ 3250 */
#include<iostream>
#include<cstdio>
#include<cstring>
#include<ctime>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<string>
#include<queue>
#include<vector>
#include<stack>
#include<list>
#include<map>
#include<set>
using namespace std;
typedef unsigned long long llu;
const int maxn = 80000 + 5;
llu stc[maxn];
int main()
{
llu n,ans=0,x;
long long top=-1;
scanf("%llu",&n);
while(n--)
{
scanf("%llu",&x);
while(top>=0&&x>=stc[top]) top--;//维护一个单调递减的栈 如果大于栈顶元素 栈顶出站
stc[++top]=x;//新元素 入栈
ans+=top;
}
printf("%llu\n",ans);
}
/* POJ 2796 */
#include<iostream>
#include<cstdio>
#include<cstring>
#include<ctime>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<string>
#include<queue>
#include<vector>
#include<stack>
#include<list>
#include<map>
#include<set>
using namespace std;
#define manx maxn
const int maxn = 100000 + 5;
int l[manx],r[maxn],stc[maxn];
long long a[maxn],sum[manx];
int main()
{
int n,i;
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
sum[i]=sum[i-1]+a[i];//前缀和
l[i]=r[i]=i;
}
int top=0;
for(i=1;i<=n+1;i++)//注意n+1 因为下面r[stc[top]]=i-1;
{
while(top&&a[i]<=a[stc[top]])//维护单调递增栈
{
r[stc[top]]=i-1;//确定右区间坐标
top--;
}
l[i]=stc[top];//确定左区间坐标
stc[++top]=i;
}
long long temp,ans=-1,ans1=0,ans2=0;
for(i=1;i<=n;i++)
{
temp=(sum[r[i]]-sum[l[i]])*a[i];//枚举每个区间的值
if(temp>=ans)
{
ans=temp;
ans1=l[i];
ans2=r[i];
}
}
printf("%lld\n%lld %lld",ans,ans1+1,ans2);
}
/* POJ 2559 || HDU 1506 */
#include<iostream>
#include<cstdio>
#include<cstring>
#include<ctime>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<string>
#include<queue>
#include<vector>
#include<stack>
#include<list>
#include<map>
#include<set>
const int MAXN = 100000 +10;
using namespace std;
int l[MAXN],r[MAXN],s[MAXN];
int main()
{
long long a[MAXN];
int n;
int top;
while(scanf("%d",&n),n)
{
long long ans=0;
for(int i=1; i<=n; i++)
{
scanf("%lld",&a[i]);
l[i]=r[i]=i;
}
top=0;
for(int i=1; i<=n; i++)//确定左坐标 就是i点左边的 低或者等于这个矩形的位置
{
while(top&&a[i]<=a[s[top]])//每次我们加入一个点 和这个区间最小的点比较(及栈顶元素)
top--;
if(top==0)//栈空
l[i]=1;
else l[i]=s[top]+1;
s[++top]=i;
}
top=0;
for(int i=n; i>=1; i--)//确定右坐标 同上
{
while(top&&a[i]<=a[s[top]])//维护一个递增栈
top--;
if(top==0)//栈空
r[i]=n;
else r[i]=s[top]-1;
s[++top]=i;
}
//for(int i=1;i<=n;i++)
//cout<<l[i]<<" "<<r[i]<<"****"<<endl; //可以看相应点的坐标l[i]是记录 i点左边最小的点坐标(含等于) r[i]是最大的(含等于)
for(int i=1; i<=n; i++)
{
long long temp=a[i]*(r[i]-l[i]+1);//枚举每个区间的值
if(temp>ans)
ans=temp;
}
cout<<ans<<endl;
}
}
最后一个题画图辅助理解更好。总之强调一个单调性,单调栈不同普通枚举 时间得到了优化。当然是用STLstack也是可以的,只是比数组模拟的速度慢。
过一阵子,熟悉了DP题 在来更新单调队列优化DP吧,现在看到DP头就大