📜  门| GATE-CS-2005 |问题25(1)

📅  最后修改于: 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这两个位置。除此之外,本题就是一个简单的最大子序和算法。