版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
问题描述:
给定一个数组序列, 需要求选出一个区间, 使得该区间是所有区间中经过如下计算的值最大的一个:
区间中的最小数 ×区间所有数的和 最后程序输出经过计算后的最大值即可,不需要输出具体的区间。如给定序列 [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;
输出描述:
输出数组经过计算后的最大值。
示例1
输入
3
6 2 1输出
36
思路:
方法一:暴力求解,遍历数组,以数组中每个数作为区间第一个数往后延伸,边延伸边计算区间最小值和区间和,时间复杂度为
,(这种方法python实现会超时,只能AC 60%,循环中加入break可以AC 70%)
实现如下:
def calca_multi():
n = int(input())
nums = list(map(int, input().split()))
max_value = float("-inf")
for i in range(n):
min_value = nums[i]
sum_value = 0
for j in range(i, n):
# 如果区间有数为0,那么它一定是区间最小值,相乘结果也为0,可以无须往后扩大区间
if nums[j] == 0:
break
sum_value += nums[j]
if nums[j] < min_value:
min_value = nums[j]
max_value = max(max_value, sum_value * min_value)
print(max_value)
方法二:
类似于问题求直方图中最大矩形面积,使用单调栈保存数组下标,具体原理这里不再赘述,下面只说明出入栈规则(栈中保存的是下标,但是下面为了表述方便,栈中元素即表示栈中下标指示的数组元素):
- 如果栈为空或者当前数大于等于栈顶元素,下标入栈
- 如果当前数小于栈顶元素,或者遍历结束但栈不为空,栈顶元素出栈,作为区间最小值,同时它也是区间的右边界,区间的左边界为出栈后栈顶下标+1。
具体代码实现如下:
def calca_multi():
n = int(input())
nums = list(map(int, input().split()))
if n == 0:
return 0
# 预处理,先将累计和保存;若在出栈时再计算区间和,超时AC 80%
pre_sum = [nums[0]]
for i in range(1, n):
pre_sum.append(pre_sum[-1] + nums[i])
# 先将第一个数字下标压入栈
index_stack = [0]
max_multi = 0
curindex = 1
# 数组遍历结束但是栈不为空时仍要继续
while curindex < n or (curindex == n and len(index_stack) > 0):
# 当栈为空或者当前数大于等于栈顶元素,下标入栈
if curindex < n and (len(index_stack) == 0 or nums[curindex] >= nums[index_stack[-1]]):
index_stack.append(curindex)
curindex += 1
# 当数组遍历结束或者当前数小于栈顶元素时
else:
# 区间最小值为栈顶元素,出栈
min_num = nums[index_stack.pop()]
# 如果此时栈为空了,表示已出栈的数字比前面所有数字都小,左边界下标也就是0,否则左边界下标为(下一个元素的索引+1)
cursum = pre_sum[curindex-1] - (pre_sum[index_stack[-1]] if len(index_stack) > 0 else 0)
curmax = min_num * cursum
max_multi = max(max_multi, curmax)
return max_multi
print(calca_multi())