📜  大小范围为[L,R]的最大和子数组(1)

📅  最后修改于: 2023-12-03 15:08:11.436000             🧑  作者: Mango

最大和子数组

最大和子数组问题是一个经典的问题,在算法竞赛和面试中都经常出现。给定一个长度为N的数组,要求找出其中连续的一段,使得这段的元素之和最大,并输出这个最大的和。通常还会有一个限制条件,就是这个子数组的长度必须在一定范围内。

问题描述

给定一个长度为N的数组a,以及两个整数L和R,要求找出长度在[L,R]范围内的最大和子数组。

解法
方法一:暴力枚举

最朴素的思路是枚举所有可能的子数组,并更新最大和。时间复杂度为O(N^3),不过在范围较小的时候可以通过本题。

def max_sum_array(a, L, R):
    n = len(a)
    res = float('-inf')
    for i in range(n):
        for j in range(i, n):
            if L <= j - i + 1 <= R:
                res = max(res, sum(a[i:j+1]))
    return res
方法二:前缀和+双指针

由于子数组连续,我们可以用前缀和预处理出任意区间的和,然后用双指针维护一个长度为[L,R]的窗口,来获取其中每一个子数组的和。时间复杂度为O(N^2)。

def max_sum_array(a, L, R):
    n = len(a)
    s = [0]*(n+1)
    for i in range(1, n+1):
        s[i] = s[i-1] + a[i-1]
        
    res = float('-inf')
    for i in range(n):
        for j in range(i+L-1, min(i+R, n)):
            res = max(res, s[j+1] - s[i])
    return res
方法三:单调队列优化

对于双指针方法,我们可以对左右指针同时移动,使得时间复杂度减小到O(N)。具体做法是维护一个单调递增的双端队列q,同时保持窗口的长度在[L,R]范围内,每次移动左右指针时,删除队头元素,保证队列的单调性。时间复杂度为O(N)。

def max_sum_array(a, L, R):
    n = len(a)
    s = [0]*(n+1)
    for i in range(1, n+1):
        s[i] = s[i-1] + a[i-1]
        
    res = float('-inf')
    q = deque()
    for i in range(n):
        if i-L >= 0:
            q.popleft()
        while q and s[i+1] < s[q[-1]+1]:
            q.pop()        
        q.append(i)
        if i >= L-1:
            res = max(res, s[i+1] - s[q[0]+1])
    return res
总结

最大和子数组问题通常可以用前缀和或者单调队列来解决。其中前缀和方法需要O(N^2)的时间复杂度,而单调队列可以优化到O(N)。在实际工程中,大部分的问题都可以使用两种方法中的一种或者结合使用。