洛谷P2422 良好的感觉
本题我想了好久,最后在豆包的帮助下成功AC
题目
传送门
题意概述
在长度为N的正整数序列里寻找一段区间[i,j],使得这个区间里最小的数乘上这个区间的和的值最大
思路
由于我们是正整数,那么我们要使得区间尽可能的大,也就是我们可以分别给每一个数都找一个区间,使得这个区间以这个数为最小值,并且这个区间要尽可能的大,又因为我们求的是区间问题,所以我们可以使用前缀和来帮助我们计算
实现
如果找这个区间?
我们可以先定义一个l数组和r数组来分别表示这个数组左边最靠近a[i]并且小于a[i]的数和这个数组右边最靠近并且小于等于a[i]的数,这里为什么要错开符号主要是为了不重复和遗漏区间.
这里的数据结构我使用的是单调栈
如果没有找到l[i]就使用0,如果没有找到r[i]就使用n+1
//左边
for(int i=1;i<=n;i++){
while(!q.empty()&&a[i]<=a[q.top()]){
q.pop();
}
if(q.empty())l[i]=0;
else l[i]=q.top();
q.push(i);
}
while(!q.empty())q.pop();//清空
//右边
for(int i=n;i>=1;i--){
while(!q.empty()&&a[i]<a[q.top()]){
q.pop();
}
if(q.empty())r[i]=n+1;
else r[i]=q.top();
q.push(i);
}
怎么求最大值?
我们可以遍历每个a[i]并且用他们的区间来求最大值
for(int i=1;i<=n;i++){
int ll=l[i]+1;
int rr=r[i]-1;
ans=max(ans,(s[rr]-s[ll-1])*a[i]);
}
总代码
#include<bits/stdc++.h>
using namespace std;
int n;
int a[100005],l[100005],r[100005];
long long ans=0,s[100005];
stack<int>q;
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
s[i]=s[i-1]+a[i];
}
for(int i=1;i<=n;i++){
while(!q.empty()&&a[i]<=a[q.top()]){
q.pop();
}
if(q.empty())l[i]=0;
else l[i]=q.top();
q.push(i);
}
while(!q.empty())q.pop();
for(int i=n;i>=1;i--){
while(!q.empty()&&a[i]<a[q.top()]){
q.pop();
}
if(q.empty())r[i]=n+1;
else r[i]=q.top();
q.push(i);
}
for(int i=1;i<=n;i++){
int ll=l[i]+1;
int rr=r[i]-1;
ans=max(ans,(s[rr]-s[ll-1])*a[i]);
}
cout<<ans;
return 0;
}

浙公网安备 33010602011771号