问题描述
小R手上有一个长度为 n
的数组 (n > 0
),数组中的元素分别来自集合 [0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]
。小R想从这个数组中选取一段连续的区间,得到可能的最大乘积。
你需要帮助小R找到最大乘积的区间,并输出这个区间的起始位置 x
和结束位置 y
(x ≤ y
)。如果存在多个区间乘积相同的情况,优先选择 x
更小的区间;如果 x
相同,选择 y
更小的区间。
注意:数组的起始位置为 1
,结束位置为 n
。
测试样例
样例1:
输入:
n = 5, arr = [1, 2, 4, 0, 8]
输出:[1, 3]
样例2:
输入:
n = 7, arr = [1, 2, 4, 8, 0, 256, 0]
输出:[6, 6]
样例3:
输入:
n = 8, arr = [1, 2, 4, 8, 0, 256, 512, 0]
输出:[6, 7]
解题思路
题目要求从数组中选取一段连续区间,找到该区间的最大乘积及对应的起始和结束位置。由于数组中的元素是2的幂或者0,因此可以利用以下特点优化解题:
-
乘积特性:
- 元素是 2k2^k2k 的形式,乘积仍是 2和2^{\text{和}}2和 的形式。乘积越大,指数和越大。
- 若区间中包含0,乘积直接为0。因此需要在每个以0为分界点的子数组中分别计算。
-
滑动窗口和分段处理:
- 遇到0时,将数组划分为多个子数组。
- 对于每个子数组,计算所有可能的区间乘积,并记录最大值和其对应的起始和结束位置。
-
优先级规则:
- 如果最大乘积有多个区间对应,优先选起点小的;若起点相同,选终点小的。
算法步骤
-
分段处理:
- 将数组按照0进行划分。每个非零段单独处理。
- 如果数组全为0,直接返回区间长度为1的结果,且优先选择靠前的元素。
-
计算区间乘积:
- 用两个指针 lll 和 rrr 表示当前区间的左右端点,计算每个可能的区间乘积。
- 记录最大乘积和其对应的 x,yx, yx,y。
-
比较更新最大值:
- 如果发现当前区间乘积比之前的大,更新最大值和对应的区间。
- 如果乘积相同,按照起点和终点的优先级规则更新。
-
合并结果:
- 遍历所有子数组的结果,输出全局最大乘积区间。
代码实现
python
复制代码
def max_product_interval(n, arr):
max_product = -1 # 初始化最大乘积
best_x, best_y = 1, 1 # 初始化最佳区间
start = 0 # 分段起点
# 遍历整个数组,分段处理
for i in range(n + 1):
if i == n or arr[i] == 0: # 遇到 0 或到数组末尾
if start < i: # 存在非零子段
subarray = arr[start:i] # 提取非零子段
product, x, y = find_max_product_in_subarray(subarray, start)
# 更新全局最大值和区间
if (product > max_product or
(product == max_product and x < best_x) or
(product == max_product and x == best_x and y < best_y)):
max_product, best_x, best_y = product, x, y
start = i + 1 # 更新下一个子段的起点
return [best_x, best_y]
def find_max_product_in_subarray(subarray, offset):
"""
计算一个非零子数组的最大乘积区间。
返回值为 (最大乘积, 起始位置, 结束位置)。
"""
n = len(subarray)
max_product = -1
best_x, best_y = 1, 1
product = 1
for l in range(n): # 左端点
product = 1 # 重置乘积
for r in range(l, n): # 右端点
product *= subarray[r]
if product > max_product:
max_product = product
best_x, best_y = l + offset + 1, r + offset + 1 # 转换为全局位置
elif product == max_product:
if l + offset + 1 < best_x or (l + offset + 1 == best_x and r + offset + 1 < best_y):
best_x, best_y = l + offset + 1, r + offset + 1
return max_product, best_x, best_y
代码说明
-
分段处理:
- 主函数
max_product_interval
遍历数组,当遇到 0 时将当前段提取出来,调用find_max_product_in_subarray
计算当前段的最大乘积。
- 主函数
-
区间乘积计算:
- 在每个子段内,使用嵌套循环枚举所有可能的区间,逐步累乘,并记录最大乘积和对应区间。
-
优先级判断:
- 更新最大值时,检查乘积是否更大。如果乘积相同,按起点和终点的规则比较。
复杂度分析
-
时间复杂度:
- 遍历数组一次分段 O(n)。
- 每个子段计算区间乘积 O(k^2),其中 kkk 是子段长度。最坏情况下 k≈nk ,总体复杂度为O(n^2)。
-
空间复杂度:
- 使用常量额外空间,复杂度为 O(1)。
AC代码如下: