【暴力法】【分治法】【动态规划】使用【Python】解决最大子段和问题

问题描述:给定由n个整数(可能为负整数)组成的序列a1,a2,a3,...,an,寻找它的某个连续子段,使得其和最大。如(-2,11,-4,13,-5,-2)最大子段是{11,-4,13}其和为20。

【暴力法】

# 直接遍历,暴力解决
sum=0
a=[-2,11,-4,13,-5,-2]
for i in range(len(a)):   # 遍历起点
    for j in range(i,len(a)):   # 遍历终点
        thissum=0
        for k in range(i,j+1):    # 从起点遍历到终点
            thissum+=a[k]
        if(thissum>sum):
            sum=thissum
            besti=i
            bestj=j
print("最优值为:",sum)
print("最优解为:{0}至{1}".format(besti,bestj))

暴力法的时间复杂度是:O(n^{3}) 

为避免重复计算,可将此算法进行改进。

sum=0
a=[-2,11,-4,13,-5,-2]
for i in range(len(a)):
    thissum=0
    for j in range(i,len(a)):
        thissum+=a[j]
        if thissum>sum:
            sum=thissum
            besti=i
            bestj=j
print("最优值为:",sum)
print("最优解为:{0}至{1}".format(besti,bestj))

改进后的时间复杂度为O(n²) 

【分治法】

将序列a[1:n]分成长度相等的两段a[1:n/2]a[n/2+1:n],分别求出这两段的最大字段和

a[1:n]的最大子段和有三种情形:

a[1:n]的最大子段和与a[1:n/2]的最大子段和相同;// 在左边

a[1:n]的最大子段和与a[n/2+1:n]的最大子段和相同; // 在右边

a[1:n]的最大字段和为aiaj的和1<=i<=n/2n/2+1<=j<=n // 跨了中点

对于①②,直接递归

对于③,an/2an/2+1肯定在最优序列中,s1+s2即为出现情形3时的最优值,以n/2为界,左右两边向中间缩,分别找最大子段和

# 分治法
def maxSubSum(a,left,right):
    sum=0
    if left==right:
        if a[left]>0:
            sum=a[right]
        else:
            sum=0
        # sum=a[left]>0?a[right]:0
    else:
        center=(left+right)//2
        leftSum=int(maxSubSum(a,left,center))          # 找最左边最大和
        rightSum=int(maxSubSum(a,center+1,right))      # 找最右边最大和
        # 覆盖了n/2的左半边最大和
        s1=0
        lefts=0
        for i in range(center,left-1,-1):
            lefts+=a[i]
            if lefts>s1:
                s1=lefts
        # 覆盖了n/2+1的右半边最大和
        s2=0
        rights=0
        for i in range(center+1,right+1):
            rights+=a[i]
            if rights>s2:
                s2=rights
        sum=s1+s2                 # 跨了中点情况的最大值
        if sum<leftSum:
            sum=leftSum
        if sum<rightSum:
            sum=rightSum
        sum=max(sum,leftSum,rightSum)    # 求三者最大
    return sum
if __name__=='__main__':
    a = [-2, 11, -4, 13, -5, -2 ]
    left=0
    right=len(a)-1
    print(maxSubSum(a,left,right))

分治法的时间复杂度是O(nlogn)

【动态规划】

计算b[j]的动态递归式为b[j]=max{b[j-1]+a[j],a[j]} 1≤j≤n

据此,可设计出的最大子段和的动态规划算法如下:

# 动态规划
sum=0
b=0
i=0
a=[-2,11,-4,13,-5,-2]
for j in range(len(a)):
    if b>0:             # 如果b大于0,就进行累加操作
        b+=a[j]
    else:               # 否则b=a[j],从这里开始累加,利用i来记录这里j的值
        b=a[j]
        i=j
    if b>sum:           # 判断最大值为最优值
        sum=b
        i2=i
        j2=j
print("最优值为:",sum)
print("最优解为:{0}至{1}".format(i2,j2))

此动态规划主要记录前一次的值,然后进行最大值比较,此算法的时间复杂度为O(n)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值