918. Maximum Sum Circular Subarray

解法

解法LC上写得很详细了,大概概括一下吧。
主要有2类算法,分类的原则是如何表示循环数组的子串,一种是A*2,第二种是2区间

A*2

遇到这种循环题,我的第一反应就是把拼两个A,得到新数组,这样问题转化为新数组的最大子串和问题
最大子串和问题前面做过了,可以用两个前缀和相减来做: a n s [ j ] = max i , j ( P [ j ] P [ i ] ) ans[j] = \max_{i,j}(P[j]-P[i])
但是由于一个元素只能在子串里出现一次,所以有一个附加条件
j i < = n j-i<=n
由于变量有两个,肯定是固定j求最好的i。

就是说,A*2的方法中,循环数组的子串可以表示为 [ i , j ] 0 i , j < 2 n     j i n [i,j],0\le i,j<2n\ 且\ j-i\le n

首先算出前缀和数组 P P ,再遍历它,当遍历到 P [ j ] P[j] 时,显然最优解是j前面长度为n的窗口的P的最小值(前面不足n个数的时候就当作窗口可以当作前面填了空元素)
是不是有点眼熟,滑动窗口最值,好像可以用队列来做:

class Solution(object):
    def maxSubarraySumCircular(self, A):
        """
        :type A: List[int]
        :rtype: int
        """
        n = len(A)
        A += A[:]
        P = [0]
        cur = 0
        for num in A:
            cur += num
            P.append(cur)

        queue = [0]
        ans = -30001
        for i in xrange(1,2*n+1):
            if i-queue[0]>n:
                queue.pop(0)

            ans = max(ans, P[i]-P[queue[0]])

            while len(queue) and P[queue[-1]]>=P[i]:
                queue.pop()

            queue.append(i)

        return ans

2-区间

2-区间主要是基于一个叫kadane算法的东西来做的
这个算法是用来求最大子串和的DP算法
状态转移方程是:
d p [ j ] = max ( d p [ j 1 ] , 0 ) + a [ j ] dp[j] = \max(dp[j-1], 0) + a[j]
其中, d p [ j ] dp[j] 代表的是以 a [ j ] a[j] 结尾的所有子串的最大和
它可以写成一个省空间的形式:

cur = -INF
ans = -INF
for num in A:
  cur = max(cur,0)+a[j]
  ans = max(ans, cur)

当数组A不是循环数组的时候,所有的子串可以表示成 [ i , j ] [i,j]
但是当A是循环数组的时候,一部分子串可以这么表示,另一部分子串只能表示成: [ j , n 1 ] + [ 0 , i ] [j,n-1]+[0,i] 。我们把前者叫1-区间,后者叫这就是所指的2-区间。

就是说,2-区间的方法中,循环数组的子串可以表示为
[ i , j ] , 0 i j < n [ 0 , i ] , [ j , n 1 ] , 0 i < j < n \begin{matrix} [i,j]&, 0\le i\le j<n\\ [0,i],[j,n-1]&, 0\le i<j<n \end{matrix}

对于1-区间,kadane算法就可以求解了
对于2-区间,有两种求解算法:

  1. R j R_j 为j往后的后缀和,固定i求最大的j:
    a n s [ i ] = P [ i ] + max j > i R j ans[i] = P[i]+\max_{j>i}R_j
  2. 通过求补集[i+1,j-1]的和就可以求出2-区间的子串和,因为整个数组的总和为常数,要使2-区间子串和最大,需要使补集和最小,所以整个问题转化成2个问题:求一遍1-区间最大子串和,再求一遍1-区间最小子串和(子串不可以为空,但不能为全集)。子串不能为空是因为为空的结果在1-区间里算过了,子串不能为全集就得拆成A[1:]A[:-1]

个人比较喜欢第2种做法,代码如下:

class Solution(object):
    def maxSubarraySumCircular(self, A):
        """
        :type A: List[int]
        :rtype: int
        """
        n = len(A)
        INF = 30000
        cur_max, ans_max = -INF, -INF
        cur_min1, ans_min1 = INF, INF
        cur_min2, ans_min2 = INF, INF

        for i,x in enumerate(A):
            cur_max = max(cur_max,0)+x
            ans_max = max(ans_max, cur_max)
            if i>0:
                cur_min1 = min(cur_min1,0)+x
                ans_min1 = min(ans_min1, cur_min1)
            if i<n-1:
                cur_min2 = min(cur_min2, 0) + x
                ans_min2 = min(ans_min2, cur_min2)

        all = sum(A)

        return max(ans_max, all-ans_min1, all-ans_min2)

猜你喜欢

转载自blog.csdn.net/lemonmillie/article/details/85017814
今日推荐