最长子段问题求解

问题描述: 一个N个整数的数组(A[0] ... A[n-1]), 求这个数组的子数组的最大和。

举例:

数组:A = [1, -2, 3, 5, -3, 2] 返回: 8


解法一: 穷举法

穷举出所有的子数组,分别求和,最后选出最大值

n个元素的组合有n*(n-1)种,对每种组合求和需要c次,其中c为该组合元素数

因而该算法复杂度为O(n2)*O(n) = O(n3)

假设m[i, j]表示子数组(A[i]... A[j]), 则该子数组的和等于m[i, j-1] + A[j],所以只要

知道m[i, j-1],就可以一次计算出m[i, j] 的值

所以可以改进算法, 通过避免重复计算子数组之和来降低复杂度,改进后T(n) = O(n2)

Python实现:

def SubMax_force(arr):
    m = {}
    maxnum = 0
    for i inrange(len(arr)):
       m[(i,i-1)]= 0
        for j inrange(i, len(arr)):
           m[(i,j)] = m[(i,j-1)] + arr[j]
            if m[(i, j)] > maxnum:
               maxnum = m[(i, j)]
    return maxnum

 

解法二: 分治法

把数组分为A[0]...A[n/2 - 1]和A[n/2]...A[n-1]两部分,分别递归求出其最大子段和

则A[0]...A[n-1]最大子段和可能来自与以下三个子数组之一:

1. (A[0]...A[n/2 - 1])

2. (A[n/2]...A[n-1)

3. (A[i]...A[j]) 其中i,j 跨越左右两个子数组

其中1,2 可以分别递归求解, 而3需要单独求解

对于3 可以分别求解以A[n/2]结尾的最大子段和以A[n/2+1]为首的最大子段,然后

两者相加即可。

该解法算法复杂度O(n*log2n)

Python实现:

def SubMax_divide(arr):
    if len(arr)== 1:
        returnarr[0]
    sum1 =SubMax_divide(arr[0:len(arr)/2])
    sum2 =SubMax_divide(arr[len(arr)/2: len(arr)])
    sum31 =arr[len(arr)/2-1]
    sum32 =arr[len(arr)/2]
    tmp = 0
    for i inrange(len(arr)/2)[::-1]:
        tmp +=arr[i]
        if tmp> sum31:
            sum31= tmp
    tmp = 0
    for i inrange(len(arr)/2, len(arr)):
        tmp +=arr[i]
        if tmp> sum32:
            sum32= tmp
    sum3 =sum31+sum32                  
    returnmax(max(sum1,sum2),sum3)

解法三: 动态规划

动态规划的两个关键因素是“最优子结构”和“重叠子问题”

“最优子结构”指的是一个问题的最优解包含子问题的一个最优解

对于本例,考虑A[i]...A[j]的拥有最大和的子段必定是以下三种情况之一:

1.该子段仅包含A[i]

2.该子段包含且不仅A[i], 则该子段中除去A[i]的部分必然是A[i+1]...A[j]的以A[i+1]为首的拥有最大和的子段

3.该子段不包含A[i],则该子段必然是A[i+1]...A[j]的拥有最大和的子段

以上描述表明该问题拥有最优子结构,且该问题包含两种子问题:

以A[i]为首的最大和子段,记为Start[i]

A[i]...A[j]的最大和子段,记为m[i, j]

该算法复杂度为O(n),使用了两个数组存储中间值

Python实现:

def SubMax_dp(arr):
    Start = {}
    m = {}
    m[len(arr)-1]= Start[len(arr)-1] = arr[len(arr)-1]
    for i inrange(len(arr)-1)[::-1]:
        Start[i]= max(arr[i],Start[i+1] + arr[i])
        m[i] =max(Start[i], m[i+1])
    return m[0]

空间优化:

由于Start[i],m[i]仅依赖于数组的后一个值Start[i+1],m[i+1],且数组从后向前遍历,所以可以把数组改为中间值存储

def SubMax_dp_leSp(arr):

    m= Start = arr[len(arr)-1]
    for i in range(len(arr)-1)[::-1]:
        Start = max(arr[i],Start + arr[i])
        m = max(Start, m)
        print 'i =',i,'Start =',Start,'m =',m
    return m


编程之美:

一下算法表明了此问题的一个有趣的解法,即从一段向另一端遍历数组,并累计和,若和为负值,则丢弃所有的元素,并从头开始计算,直到最后一个大于零的元素为止

def SubMax_beauty(arr):
    Start = arr[len(arr)-1]
    MAX = 0
    for i in range(len(arr)-1)[::-1]:
        if Start < 0:
            Start = 0
        Start += arr[i]
        if Start > MAX:
            MAX = Start
    return MAX
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值