题目描述
给定一个数组序列, 需要求选出一个区间, 使得该区间是所有区间中经过如下计算的值最大的一个:
区间中的最小数 * 区间所有数的和最后程序输出经过计算后的最大值即可,不需要输出具体的区间。如给定序列 [6 2 1]则根据上述公式, 可得到所有可以选定各个区间的计算值:
[6] = 6 * 6 = 36;
[2] = 2 * 2 = 4;
[1] = 1 * 1 = 1;
[6,2] = 2 * 8 = 16;
[2,1] = 1 * 3 = 3;
[6, 2, 1] = 1 * 9 = 9;
从上述计算可见选定区间 [6] ,计算值为 36, 则程序输出为 36。区间内的所有数字都在[0, 100]的范围内。
输入描述
第一行输入数组序列长度n,第二行输入数组序列。
对于 50%的数据, 1 <= n <= 10000;
对于 100%的数据, 1 <= n <= 500000;
输出描述
输出数组经过计算后的最大值。
解题思路
对于数组序列的探索,如果是一个递增序列,比如3,5,6,最小值是4的时候和可以是3,8,14,很显然最大的是9的情况,最小值是5的时候,和可以是5,11,显然和为11的情况最大,按照这个思路可以发现对于递增的序列,只需要从最大值开始,计算序列[6],[5,6],[3,5,6]。可以发现,解题的关键在于以一个数num作为最小值时,以num允许达到的最大区间来计算,比如以3为最小值,计算到6。比如说3,5,6,4,2这个序列,到4的时候序列不再增长,因此先计算前面的递增序列,[6],[5,6],[3,5,6]暂时不需要计算,因为4大于3,3的区间扩展还没有结束,同理,4也没有结束,需要继续向下比较,2<4,因此4作为最小值的区间从0开始到3,计算[5,6,4],同理2<3,3 的区间扩展已经结束,计算[3,5,6,4]。2作为最小值是[3,5,6,4,2]。
按照上面的解释,我们需要一个数据结构来判断一个数在序列中能够连续小到哪一个位置,还知道任意位置[i,j]的和。数据结构使用栈,遍历数组data,当前值data[i],如果栈为空,向其中添加当前元素,否则比较栈顶元素和data[i],如果栈顶元素小于data[i],那么该值入栈,说明栈里的元素的区间扩展还可以继续,否则,说明从stack.top()向左到第一个小于data[i]的值之间的每一个数区间扩展已经完毕,那么将其出栈并计算。由于需要的是一个元素作为最小值的区间,所以栈中存放的是元素对应的索引值。
为了保证一定结束,在数组最后加一个最小值0,既不会改变序列的最大值,也能够保证扩展区间一定会结束。
实现代码
#include<iostream>
#include <stack>
#include <vector>
using namespace std;
int max(int a,int b){
return a>b?a:b;
}
int main(){
int n,i;
cin>>n;
vector<int> data(n+2,0);
vector<int> sum(n+1,0);
for(i=1;i<=n;i++){
cin>>data[i];
sum[i]=sum[i-1]+data[i];
}
data[n+1]=0;
i=1;
stack<int> stk;
int maxv=0;
for(;i<=n+1;){
if(stk.empty()||data[i]>=data[stk.top()])
stk.push(i++);
else{
int h=stk.top();
stk.pop();
int sump=(stk.empty())?sum[i-1]:(sum[i-1]-sum[stk.top()]);
maxv=max(maxv,sump*data[h]);
}
}
cout<<maxv<<endl;
return 0;
}
这道题最主要的就是找到区间扩展的方法,使用stack找到一段序列中的最小值。类似的用栈来记录一段序列的最值问题,见https://blog.csdn.net/qq_41922018/article/details/104628287