一维动态规划-连续子序列和最大值

1.问题引入

前面介绍了利用分治算法求解股票买卖问题,并且已经知道这个问题可以转化为求连续子序列和的最大值问题。如股票价格为 [ 10 , 11 , 7 , 10 , 6 ] [10,11,7,10,6] [10,11,7,10,6],那么前后两日的收益差值为 [ 1 , − 4 , 3 , − 4 ] [1,-4,3,-4] [1,4,3,4]。原问题就变换成给定一个序列,找出其中连续累加值最大的子序列,序列 [ 1 , − 4 , 3 , − 4 ] [1,-4,3,-4] [1,4,3,4] [ 3 ] = 3 [3]=3 [3]=3即为连续累加值最大的子序列,意味着在股票价格为7的时候买入,股价为10的时候卖出将获得最佳收益,收益值为3。
当给定n个元素的序列A,求和最大的连续子序列时,输入序列元素必须包含负值才有意义。否则当输入序列均为正值,那么连续子序列和最大的就是原序列本身。
该问题最简单的算法就是穷举给定序列的所有子序列,然后求得子序列累加和的最大值。对于有 n n n个元素的序列,其所有连续子序列的个数为 n 2 n^2 n2说明:n个元素的序列,每一个元素都可以作为连续子序列起点和终止点,而每一个起点可以对应n个终止点,故连续子序列的个数共有n2。该问题也可以用分治法来求解,分治算法的时间复杂度为 O ( n   l o g   n ) O(n\,log\,n) O(nlogn)。下面将根据动态规划求解问题的基本步骤,介绍时间复杂度为 O ( n ) O(n) O(n)的算法。

2.定义子问题

该问题输入的是一个有 n n n个元素的序列 A A A,不妨设 P ( i ) P(i) P(i)为直到第 i i i个元素的最大和,也就是将输入序列的前缀作为子问题。每一个元素就对应一个子问题,子问题的个数为 Θ ( n ) \Theta(n) Θ(n)

3.猜测解

对于子问题 P ( i ) P(i) P(i),考察元素 A [ i ] = − 5 A[i]=-5 A[i]=5,如图9.6所示。该元素要么在子问题的解中,要么不在,也就是:

  • 子问题 P ( i ) P(i) P(i)的解包括第 i i i个元素,如图( 1 1 1)第二行所示。此时有 P ( i ) = P ( i − 1 ) + A ( i ) P(i)=P(i-1)+A(i) P(i)=P(i1)+A(i);
  • 子问题 P ( i ) P(i) P(i)的解不包括第 i i i个元素(即包括第 i i i个元素不是最大和,需要更换起点为,此时起止点重合在 A [ i ] A[i] A[i]取得最大和),如图(1)第三行所示。意味着需要从第 i i i个元素开始重新计算值,即 P ( i ) = A ( i ) P(i)=A(i) P(i)=A(i)

− 2 11 − 4 13 − 5 ) ‾ − 2 11 − 4 13 ) − 5 − 2 11 − 4 13 ( − 5 (1) \underline{\begin{array}{c} \begin{matrix} -2& 11& -4& 13\\ \end{matrix}-\left. 5 \right)\\ \end{array}} \\ \begin{matrix} -2& 11& -4& \left. 13 \right)& -5\\ \end{matrix} \\ \begin{matrix} -2& 11& -4& 13& \left( -5 \right.\\ \end{matrix} \tag 1 2114135)211413)5211413(5(1)

4.子问题之间的递归关系

根据以上分析,可以建立子问题间的递归关系如下:

P ( i ) = m a x { P ( i − 1 ) + A ( i ) , A ( i ) } \begin{aligned} P(i)=max\{P(i-1)+A(i),A(i)\} \end{aligned} P(i)=max{P(i1)+A(i),A(i)}
初始条件为
P ( 0 ) = 0 \begin{aligned} P(0)=0 \end{aligned} P(0)=0
这个递归式与之前拾捡硬币问题的递归式类似,都是当前子问题的解只与已经求解的子问题有关,也就是递归求解的过程满足拓扑排序。

5.自底向上构造动态规划表

对于每一个子问题的解,由于只与已经求出的子问题解有关。也就是说,如果将个子问题间的关系画成图,求解该问题的过程就相当于在图上做拓扑排序。因此,可自底向上实现的递归关系,见代码6。下表是输入序列 A = [ − 2 , 11 , − 4 , 13 , − 5 , 2 ] A=[-2, 11, -4, 13, -5, 2] A=[2,11,4,13,5,2]时,自底向上构造的动态规划表table

i0123456
table(i)0-2117201517

6. Python实现时间复杂度O(n)的算法求解连续子序列和最大值

代码6

def bottom_up_cont_sub_seq(seq):
    """
    自底向上实现时间复杂度O(n)的算法求解连续子序列和最大值
    :param seq:原始序列
    :return: 连续子序列和最大值,连续子序列和最优解
    """
    item_nums = len(seq)
    table = [0] * (item_nums + 1)  # 初始化动态规划表
    table[1] = seq[0]  # 表中第2项为序列第1项
    global_best = []  # 初始化最优解序列
    for i in range(2, item_nums + 1):
        table[i] = max(table[i - 1] + seq[i - 1], seq[i - 1])  # 由于表长比序列大1,所以当前元素索引为i-1
    max_sum = max(table)
    ind_max = table.index(max_sum)
    while ind_max >= 1:
        # 表当前元素值等于表左边子问题的值+序列当前元素值(索引比表小1)
        if table[ind_max] == table[ind_max - 1] + seq[ind_max - 1]:
            global_best.append(seq[ind_max - 1])
            ind_max -= 1
        # 达到起点终止
        else:
            global_best.append(seq[ind_max - 1])
            break
    return max_sum, global_best[::-1]

7.测试

if __name__ == '__main__':
    sequence = [-2, 11, -4, 13, -5, 2]
    max_value,global_best_sub_seq = bottom_up_cont_sub_seq(sequence)
    print("原始序列为:{}\n连续子序列和最大值为:{}\n连续子序列和最优解为:{}".format(sequence,max_value,global_best_sub_seq))

8.运行截图

在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值