您的位置:首页 > 其它

2018今日头条内推笔试2

2017-08-23 13:22 513 查看


对于这道题,假设     区间最小数*区间所有数的和    记为一个区间的K值

我们首先要明确一点:假设有一个区间,它的最小值为a,我们对这个区间进行扩张,当扩张的值大于等于a时,这个区间的K值只增不减,这个很好理解。明白这点之后,我们就不需要遍历序列的所有子区间了,只需要遍历所有扩张之后的区间即可得到最大的K值

接下来,我们依次以序列中的每个值作为区间的最小值,并且以这个值作为基准,向两边扩张,直到遇到比这个值小的数或者到达序列的一端停止,之后我们计算扩张之后每个区间的K值,得到最大的K值就是题目所要求的。

例如,以图片题中给定的序列[6,2,1]为例,

1.我们以6为基准,左边无法扩张,右边2比6小,无法扩张,所以扩张之后的区间为[6],得到K值为6*6=36;

2.我们以2为基准,左边扩张到6,右边1比2小,无法扩张,所以扩张之后的区间为[6,2],得到K值为2*(2+6)=16

3.我们以1为基准,扩张之后的区间为[6,2,1],K值为1*(1+2+6) = 9

最终得到最大K值为36;

得到解题的思路之后,我们需要考虑的是如何去计算扩张之后区间的K值。这个过程我们使用一种特殊的数据结构:单调栈

我们简单解释一下什么是单调栈:规定栈中元素从栈底到栈顶必须是严格递增的或者严格递减的栈。如果入栈元素不符合栈的单调性,那么我们依次将栈顶元素出栈,直至满足入栈条件或者栈空。对于每一个出栈的元素a,使它出栈的元素就是a右侧离a最近的不大于a的数,a出栈后,栈顶元素就是a左侧离a最近的小于a的数,如果a出栈后栈为空,则表示a左侧没有比a小的数。

根据严格递增的单调栈,每次出栈一个元素,我们就可以找到这个元素两侧离它最近比它小的位置,从而得到以该元素为区间最小值扩张之
4000
后的区间,进而可以求出区间K值。

我们继续用题中的例子:对于数列[6,2,1]我们使用单调递增的栈存储

1.我们把6存进栈中

2.我们想要把2存进栈中,但是违反了栈的单调性,所以先把栈顶元素6出栈,此时,我们计算以6为最小值的扩张区间的K值,由于此时栈为空,所以扩张区间左侧起始位置为0,由于是2导致6出栈,所以扩张区间右侧为0,所以区间从0到0,只有一个元素6,K值为36;

3.计算完以6为最小值的扩张区间之后,我们把2入栈,接下来我们想把1入栈,1<2,违反栈的单调性,同上一步,我们把2出栈,由于此时栈为空,所以扩张区间左侧起始位置为0,由于是1导致2出栈,所以扩张区间右侧为1,所以区间从0到1,K值为16

4.然后,我们把1入栈,由于数列所有元素已经全部入栈,这时候,我们依次把栈中元素出栈,计算K值(方法步骤同上)……

有一点需要注意:由于我们需要的是区间的位置,所以在单调栈中我们存放的是数组元素的下标

下面结合代码理解一下:

import java.util.*;
public class Main{
public static void main(String[] args){
Scanner in = new Scanner(System.in);
while(in.hasNext()){
int n = in.nextInt();
int[] a = new int[n+1];
in.nextLine();
for(int i=0;i<n;i++){
a[i] = in.nextInt();
}
a
= -1;                       //数组最后加上一个元素-1,使得栈中没出栈的元素全部出栈
int max = 0;
Stack<Integer> stack = new Stack<>();
for(int i=0;i<=n;i++){
while(!stack.isEmpty() && a[i]<=a[stack.peek()]){
int temp = a[stack.pop()];                  //出栈元素作为扩张区间的最小值
int start = stack.isEmpty() ? -1 : stack.peek();
int sum = 0;
for(int j=start+1;j<i;j++){          //计算扩张区间所有元素的和
sum += a[j];
}
max = Math.max(max, temp*sum);
}
stack.push(i);
}
System.out.println(max);
}
}
}

使用单调栈这种数据结构能够降低时间复杂度,优化算法。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: