这道题不是很难,只是太久没有敲过ST表和单调栈,就当贴个板子咯。而且,这道题的做法有很多,也比较常用,所以写一写这个题的题解吧 (*^__^*) 嘻嘻~
题目链接:洛谷 P2422
题目大意:找出一段区间,使得“区间最小值×区间和”最大。
题解:
单调栈
这种题有一个常见的思路是用枚举最小值是谁,然后就能确定区间长度。所以可以用正反两遍单调栈,处理出某个点作为最小值时左右两边各能扩展到哪里。最后找最大的a[i]*(sum[r[i]]-sum[l[i]-1])。
还有一种方法是之维护一个顶部最小的单调栈,需要弹出栈顶时即栈顶左右两侧比它小的位置都已经确定,更新一次答案即可。分治+ST表
这种把区间分开的思路也比较好用。从最小的元素开始,此时最优的区间即整个区间,直接 min*(r-l+1),然后,最小值的位置把序列分成两部分(或者一部分,即取序列端点元素时),每一部分再找最小值,乘区间和,递归求解。f[][]是ST表记录区间最小值,g[][]记录最小值的位置。
下面贴代码 *^_^ * ~~~(有参考大牛们(⊙v⊙))
( 4种,暴力 / ST表 / 两遍单调栈 / 一遍单调栈)
code_1——暴力
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
inline int read()
{
char c=getchar(); int num=0,f=1;
while (c<'0'||c>'9') { if (c=='-') f=-1; c=getchar(); }
while (c<='9'&&c>='0') { num=num*10+c-'0'; c=getchar(); }
return num*f;
}
int n,a[100005];
long long ans=0;
int main()
{
n=read();
for (int i=1;i<=n;i++) a[i]=read();
for (int i=1;i<=n;i++)
{
long long sum=a[i],l=i-1,r=i+1;
while (a[l]>=a[i]&&l>=1)
sum+=a[l],l--;
while (a[r]>a[i]&&r<=n)
sum+=a[r],r++;
ans=max(ans,sum*a[i]);
}
cout<<ans;
return 0;
}
code_2——ST表
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 100005
using namespace std;
inline int read()
{
char c=getchar(); int num=0,f=1;
while (c<'0'||c>'9') { if (c=='-') f=-1; c=getchar(); }
while (c<='9'&&c>='0') { num=num*10+c-'0'; c=getchar(); }
return num*f;
}
int n,tpa,tpb,f[N][22],g[N][22];
long long sum[N],ans=0;
void find(int l,int r)
{
int k=log(r-l+1)/log(2);
tpa=min(f[l][k],f[r-(1<<k)+1][k]);
tpb=(tpa==f[l][k])?g[l][k]:g[r-(1<<k)+1][k];
ans=max(ans,(sum[r]-sum[l-1])*tpa);
if (l==r) return;
if (tpb!=l) find(l,tpb-1);
if (tpb!=r) find(tpb+1,r);
}
int main()
{
n=read();
for (int i=1;i<=n;i++)
{
f[i][0]=read();
g[i][0]=i;
sum[i]=sum[i-1]+f[i][0];
}
for (int j=1;j<=19;j++)
for (int i=1;i+(1<<j)-1<=n;i++)
{
f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][i-1]);
g[i][j]=(f[i][j]==f[i][j-1])?g[i][j-1]:g[i+(1<<(j-1))][j-1];
}
find(1,n);
printf("%lld",ans);
return 0;
}
code_3——两遍单调栈
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
inline int read()
{
char c=getchar(); int num=0,f=1;
while (c<'0'||c>'9') { if (c=='-') f=-1; c=getchar(); }
while (c<='9'&&c>='0') { num=num*10+c-'0'; c=getchar(); }
return num*f;
}
int n,a[100005],top=0,sta[100005],l[100005],r[100005];
long long sum[100005],ans=0;
int main()
{
n=read();
for (int i=1;i<=n;i++)
{
a[i]=read();
sum[i]=sum[i-1]+a[i];
}
for (int i=1;i<=n;i++)
{
while (top&&a[sta[top]]>a[i]) r[sta[top--]]=i;
sta[++top]=i;
}
top=0;
for (int i=n;i>=1;i--)
{
while (top&&a[sta[top]]>a[i]) l[sta[top--]]=i;
sta[++top]=i;
}
for (int i=1;i<=n;i++)
ans=max(ans,a[i]*(sum[r[i]-1]-sum[l[i]]));
cout<<ans;
}
code_4——一遍单调栈
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
inline int read()
{
char c=getchar(); int num=0,f=1;
while (c<'0'||c>'9') { if (c=='-') f=-1; c=getchar(); }
while (c<='9'&&c>='0') { num=num*10+c-'0'; c=getchar(); }
return num*f;
}
int n,top=0;
int sta[100005],ne,a[100005];
long long sum[100005],ans=0;
int main()
{
n=read();
for (int i=1;i<=n;i++)
{
a[i]=read();
sum[i]=sum[i-1]+a[i];
while (top&&(a[sta[top]]>=a[i]))
{
ans=max(ans,(sum[i-1]-sum[sta[top-1]])*a[sta[top]]);
top--;
}
sta[++top]=i;
}
while (top)
{
ans=max(ans,(sum[n]-sum[sta[top-1]])*a[sta[top]]);
top--;
}
cout<<ans;
}