【动态规划】连续子序列的最大和,附二分法求解

文章介绍了如何找到数组中连续子序列的最大和,包括直接枚举、改进的枚举法、动态规划和二分法四种方法,并分析了它们的时间复杂度。动态规划方法在效率上优于枚举法,而二分法进一步优化了时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

给定以数组,找出连续子序列的最大和
例如,

nums = [3, -4, 2, -1, 2, 6, -5, 4]

其中,最大的子序列为 [2,−1,2,6][2, -1, 2, 6][2,1,2,6],结果为 999

1 枚举

1.1 直接枚举

枚举法可以分成两个部分:
① 计算位置iii到位置jjj这段连续子序列的和,记为SCS[i][j]SCS[i][j]SCS[i][j]
② 双指针循环遍历iiijjj

具体代码如下:

def SCS(nums, i, j):
    scs = 0
    for t in range(i, j + 1):
        scs += nums[t]
    return scs


def max_of_SCS(nums):
    n = len(nums)
    max_len = 0
    for i in range(n):
        for j in range(i, n):
            max_len = max(max_len, SCS(nums, i, j))
    return max_len


nums = [3, -4, 2, -1, 2, 6, -5, 4]
print(max_of_SCS(nums))

算法时间复杂度:O(n3)O(n^3)O(n3)

1.2 改进

O(n3)O(n^3)O(n3)的时间复杂度太高了,通过观察可以发现,在最内层循环的时候,并不需要分别去计算每个SCS[i][j]SCS[i][j]SCS[i][j]
而是在遍历jjj的时候将从iiijjj的最大SCSSCSSCS保存下来即可

修改代码如下:

def max_of_SCS(nums):
    n = len(nums)
    max_Sum = 0
    for i in range(n):
        this_Sum = 0
        for j in range(i, n):
            this_Sum += nums[j]
            max_Sum = max(max_Sum, this_Sum)
    return max_Sum


nums = [3, -4, 2, -1, 2, 6, -5, 4]
print(max_of_SCS(nums))

算法时间复杂度:O(n2)O(n^2)O(n2)

2 动态规划

2.1 最优性条件

假设存在一个最大和连续子序列CS[i][j]CS[i][j]CS[i][j],则以i−1i-1i1为结尾的连续子序列CS[∗][i−1]CS[*][i-1]CS[][i1],和以j+1j+1j+1为开头的连续子序列CS[j+1][∗]CS[j+1][*]CS[j+1][]的连续子序列的最大和一定0。
即,
CS[∗][i−1]≤0,∀∗∈[0,i−2]CS[*][i-1] \le 0, \forall * \in [0, i-2]CS[][i1]0,[0,i2]
CS[j+1][∗]≤0,∀∗∈[j+2,n−1]CS[j+1][*] \le 0, \forall * \in [j+2, n-1]CS[j+1][]0,[j+2,n1]

2.2 最优性条件证明

反证法:
假设存在一个最大和连续子序列CS[i][j]CS[i][j]CS[i][j],并且存在CS[t][i−1]>0,t∈[0,i−2]CS[t][i-1] > 0, t \in [0,i-2]CS[t][i1]>0,t[0,i2]
CS[t][j]CS[t][j]CS[t][j]=CS[t][i−1]+CS[i][j]>CS[i][j]CS[t][i-1]+CS[i][j]>CS[i][j]CS[t][i1]+CS[i][j]>CS[i][j]
CS[i][j]CS[i][j]CS[i][j]是最大值矛盾

2.2 状态更新

将从000iii中的连续子序列的最大和记为MSCS[i]MSCS[i]MSCS[i]
MSCS[0]MSCS[0]MSCS[0]=num[0]num[0]num[0]
状态更新函数可以定义为:
① 如果MSCS[i−1]>0MSCS[i-1]>0MSCS[i1]>0
MSCS[i]MSCS[i]MSCS[i]=MSCS[i−1]+num[i]MSCS[i-1]+num[i]MSCS[i1]+num[i]
② 如果MSCS[i−1]≤0MSCS[i-1] \le 0MSCS[i1]0
MSCS[i]MSCS[i]MSCS[i]=0+num[i]0+num[i]0+num[i]

2.3 代码

def max_of_SCS(nums):
    n = len(nums)
    MSCS = [0] * n
    MSCS[0] = nums[0]
    for i in range(1, n):
        if MSCS[i-1] > 0:
            MSCS[i] = MSCS[i-1]+nums[i]
        else:
            MSCS[i] = nums[i]
    return max(MSCS)

nums = [3, -4, 2, -1, 2, 6, -5, 4]
print(max_of_SCS(nums))

算法时间复杂度:O(n)O(n)O(n)

3 二分法

3.1 二分规则

假设将数组nums0,n−1nums_{0,n-1}nums0,n1二分为nums0,mnums_{0,m}nums0,mnumsm+1,n−1nums_{m+1,n-1}numsm+1,n1,则连续子序列的最大和有三种情况:

  1. 左子数组的连续子序列的最大和,记为MSCS[0][m]MSCS[0][m]MSCS[0][m]
  2. 右子数组的连续子序列的最大和,记为MSCS[m+1][n−1]MSCS[m+1][n-1]MSCS[m+1][n1]
  3. 左子数组中以mmm为结尾的连续子序列的最大和+++右子数组中以m+1m+1m+1为开头的连续子序列的最大和,记为MSCS[i][m]+MSCS[m+1][j],i∈[0,m−1],j∈[m+2,n−1]MSCS[i][m]+MSCS[m+1][j],i \in [0,m-1],j \in [m+2,n-1]MSCS[i][m]+MSCS[m+1][j],i[0,m1],j[m+2,n1]

3.2 思路

函数MSCS(nums,i,j)MSCS(nums, i, j)MSCS(nums,i,j)
如果i==ji==ji==j,返回nums[i]nums[i]nums[i]
否则,按照返回上面的三种情况的最大值。

3.3 代码

def MSCS(nums, i, j):
    if i == j:
        return nums[i]
    m = (i+j) // 2
    max_l = MSCS(nums, i, m)
    max_r = MSCS(nums, m+1, j)
    max_mix_l = 0
    mix_l = 0
    for t in reversed(range(i, m+1)):
        mix_l += nums[t]
        max_mix_l = max(max_mix_l, mix_l)
    max_mix_r = 0
    mix_r = 0
    for t in range(m+1, j+1):
        mix_r += nums[t]
        max_mix_r = max(max_mix_r, mix_r)

    return max(max_r, max_l, max_mix_l+max_mix_r)

def max_of_SCS(nums):
    return MSCS(nums, 0, len(nums)-1)

nums = [3, -4, 2, -1, 2, 6, -5, 4]
print(max_of_SCS(nums))

算法时间复杂度:O(nlog2n)O(nlog_2n)O(nlog2n)

4 对比

算法时间复杂度n=8000时,耗时 / s
枚举法O(n2)O(n^2)O(n2)2.778
动态规划O(n)O(n)O(n)0.001
二分法O(nlog2n)O(nlog_2n)O(nlog2n)0.015
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值