📅  最后修改于: 2023-12-03 15:42:16.086000             🧑  作者: Mango
给定一个整数列表 a
,从列表的任意位置开始,排成一个圆圈。例如,如下图:
a = [1, 2, 3, 4, 5]
可以形成两种不同的圆圈:
1
5 2
4 3
或者
2
1 3
5 4
编写一个函数 max_sum(a)
,这个函数从列表a中选取一个子列表结果,使得该子列表的元素之和最大。但是,所选的子列表的首尾元素应该不会同时位于原始列表的末尾。例如,在上面的示例中,将 a[1:3] = [2, 3]
选为子列表的结果。注意,这里要求的子列表是连续的。
函数定义如下:
def max_sum(a: list[int]) -> int:
pass
assert max_sum([1, 2, 3, 4, 5]) == 7
assert max_sum([-1, 2, 3, -2, 5]) == 8
首先,我们可以将列表a扩张成twice_a,这个列表将包括所有可能的起始位置,例如,twice_a = a + a
。
接下来,我们可以使用一个最大子序和算法(Kadane’s Algorithm)来找到从起始位置i开始的最大子列表。
最后,我们可以遍历twice_a,找到所有满足题目要求的起始位置(不在列表的末尾),并找到其中的最大子列表。
def max_sum(a: list[int]) -> int:
n = len(a)
# 将a列表扩张成twice_a列表,包括所有可能的起始位置
twice_a = a * 2
# 执行Kadane's algorithm找到twice_a中的最大子序列和
max_so_far = 0
max_ending_here = 0
for i in range(2 * n):
max_ending_here += twice_a[i]
if max_ending_here < 0:
max_ending_here = 0
if max_so_far < max_ending_here:
max_so_far = max_ending_here
# 遍历twice_a列表,找到所有起始位置,并找到其中的最大子列表
max_sum_no_circular = max_so_far
for i in range(n):
# 当i为0或n-1时,不进行计算,因为首尾元素不能同时位于原始列表的末尾
if i == 0 or i == n-1:
continue
# 计算从i开始的最大子列表
cur_max = 0
cur_sum = 0
for j in range(i, i + n - 1):
cur_sum += twice_a[j]
if cur_sum < 0:
cur_sum = 0
if cur_max < cur_sum:
cur_max = cur_sum
# 更新最大子列表
max_sum_no_circular = max(max_sum_no_circular, cur_max)
return max_sum_no_circular
题目中有一个需要注意的地方,就是所选的子列表的首尾元素应该不会同时位于原始列表的末尾。因此,我们需要跳过0和n-1这两个位置。除此之外,本题就是一个简单的最大子序和算法。