算法复习——动态规划篇之最大子数组问题

算法复习——动态规划篇之最大子数组问题

以下内容主要参考中国大学MOOC《算法设计与分析》,墙裂推荐希望入门算法的童鞋学习!

1. 问题背景

在这里插入图片描述

子数组:数组中连续的一段序列,例如 X [ 3..7 ] X[3..7] X[3..7]

子数组和:子数组中元素的求和, X [ 3..7 ] X[3..7] X[3..7]的和就是 3 + 5 − 4 + 3 + 2 = 9 3+5-4+3+2=9 3+54+3+2=9

​ 那么,问题就是如何寻找数组 X X X中最大的非空子数组?

2. 问题定义

最大子数组问题(Max Continuous Subarray)

输入:

  • 给定一个数组 X [ 1.. n ] X[1..n] X[1..n],对于任意一对数组下标为 l , r ( l ≤ r ) l, r(l \leq r) l,r(lr)的非空子数组,其和记为 S ( l , r ) = ∑ i = l r X [ i ] S(l, r)=\sum_{i=l}^{r}X[i] S(l,r)=i=lrX[i]

输出:

  • 求出 S ( l , r ) S(l, r) S(l,r)的最大值,记为 S m a x S_{max} Smax

3. 枚举过程分析

​ 如果使用暴力枚举的话,需要两层枚举:

  • 第一层:枚举位置 i i i作为区间开头;
  • 第二层:枚举位置 j j j作为区间结尾。

​ 如果想要降低时间复杂度,就需要从两层枚举中舍弃掉一层。

​ 我们可以定义一个记号 D i D_{i} Di,表示以 X [ i ] X[i] X[i]开头的最大子数组,因此只要枚举出所有的 D i D_{i} Di,就得到了最大子数组。先看一个案例。

在这里插入图片描述

4. 规律

4.1 规律观察

  • 结尾位置相同

    • D 1 D_{1} D1, D 2 D_{2} D2, D 3 D_{3} D3
      • D 2 = X [ 2 ] + D 1 D_{2}=X[2]+D_{1} D2=X[2]+D1
      • D 1 = X [ 1 ] + D 2 D_{1}=X[1]+D_{2} D1=X[1]+D2
  • 结尾位置不同

    • D 3 , D 4 D_{3}, D_{4} D3,D4
      • D 3 = X [ 3 ] D_{3}=X[3] D3=X[3]
      • D 4 < 0 D_{4} < 0 D4<0
      • 按照结尾位置相同的规律, D 3 D_{3} D3应该等于 X [ 3 ] X[3] X[3] D 4 D_{4} D4,但是 D 4 D_{4} D4小于0,还不如直接等于 X [ 3 ] X[3] X[3]效果好

4.2 规律描述

​ 设 D i D_{i} Di为以 X [ i ] X[i] X[i]开头的最大子数组和。

  • 情况1:设 D i + 1 > 0 D_{i+1} > 0 Di+1>0时, D i = X [ i ] + D i + 1 D_{i}=X[i]+D_{i+1} Di=X[i]+Di+1
  • 情况2:设 D i + 1 ≤ 0 D_{i+1} \leq 0 Di+10时, D i = X [ i ] D_{i}=X[i] Di=X[i]

4.3 规律证明

  • 情况1:
    • D i + 1 D_{i+1} Di+1:以 X [ i + 1 ] X[i+1] X[i+1]开头的最大子数组和( D i + 1 > 0 D_{i+1} > 0 Di+1>0
    • S i + 1 S_{i+1} Si+1:以 X [ i + 1 ] X[i+1] X[i+1]开头的任一子数组和( S i + 1 ≤ D i + 1 S_{i+1} \leq D_{i+1} Si+1Di+1
    • 显然 X [ i ] + D i + 1 ≥ X [ i ] + S i + 1 X[i]+D_{i+1} \geq X[i]+S_{i+1} X[i]+Di+1X[i]+Si+1,因此 D i = X [ i ] + D i + 1 D_{i}=X[i]+D_{i+1} Di=X[i]+Di+1
    • 所以第一种情况成立
  • 情况2:
    • D i + 1 D_{i+1} Di+1:以 X [ i + 1 ] X[i+1] X[i+1]开头的最大子数组和( D i + 1 ≤ 0 D_{i+1} \leq 0 Di+10
    • S i + 1 S_{i+1} Si+1:以 X [ i + 1 ] X[i+1] X[i+1]开头的任一子数组和( S i + 1 ≤ D i + 1 S_{i+1} \leq D_{i+1} Si+1Di+1
    • 显然 X [ i ] + S i + 1 ≤ X [ i ] + D i + 1 ≤ X [ i ] X[i]+S_{i+1} \leq X[i]+D_{i+1} \leq X[i] X[i]+Si+1X[i]+Di+1X[i],因此 D i = X [ i ] D_{i}=X[i] Di=X[i]
    • 所以第二种情况成立

5. 动态规划

5.1 问题结构分析

  • 给出问题表示:
    • D [ i ] D[i] D[i]:以 X [ i ] X[i] X[i]开头的最大子数组和
  • 明确原始问题
    • S m a x = m a x 1 ≤ i ≤ n { D [ i ] } S_{max}=max_{1 \leq i \leq n}\{D[i]\} Smax=max1in{D[i]}

5.2 递推关系建立

5.2.1 分析最优(子)结构
  • D [ i ] D[i] D[i]:以 X [ i ] X[i] X[i]开头的最大子数组和
  • 情况1:当 D [ i + 1 ] > 0 D[i+1]>0 D[i+1]>0时, D [ i ] = X [ i ] + D [ i + 1 ] D[i]=X[i]+D[i+1] D[i]=X[i]+D[i+1]
  • 情况2:当 D [ i + 1 ] ≤ 0 D[i+1] \leq 0 D[i+1]0时, D [ i ] = X [ i ] D[i]=X[i] D[i]=X[i]
  • 分析递推关系,我们可以发现在情况一中, D [ i ] D[i] D[i]是依赖于 D [ i + 1 ] D[i+1] D[i+1]的, D [ i ] D[i] D[i] D [ i + 1 ] D[i+1] D[i+1]都是最大数组和,只不过 D [ i + 1 ] D[i+1] D[i+1]的规模比 D [ i ] D[i] D[i]小,所以大规模问题的最优解是依赖于小规模问题的最优解的,这就满足了动态规划最优子结构的性质。
5.2.2 构造递推公式

D [ i ] = { X [ i ] + D [ i + 1 ] , i f   D [ i + 1 ] > 0 X [ i ] , i f   D [ i + 1 ] ≤ 0 D[i]=\left\{ \begin{array}{rcl} X[i]+D[i+1], & & {if\ D[i+1] > 0}\\ X[i], & & {if\ D[i+1] \leq 0}\\ \end{array} \right. D[i]={X[i]+D[i+1],X[i],if D[i+1]>0if D[i+1]0

5.3 自底向上计算

  • 初始化: D [ n ] = X [ n ] D[n]=X[n] D[n]=X[n]

  • 递推公式:
    D [ i ] = { X [ i ] + D [ i + 1 ] , i f   D [ i + 1 ] > 0 X [ i ] , i f   D [ i + 1 ] ≤ 0 D[i]=\left\{ \begin{array}{rcl} X[i]+D[i+1], & & {if\ D[i+1] > 0}\\ X[i], & & {if\ D[i+1] \leq 0}\\ \end{array} \right. D[i]={X[i]+D[i+1],X[i],if D[i+1]>0if D[i+1]0

在这里插入图片描述

5.4 最优方案追踪

5.4.1 记录决策过程
  • 构造追踪数组 R e c [ 1.. n ] Rec[1..n] Rec[1..n] R e c [ i ] Rec[i] Rec[i]记录以 X [ i ] X[i] X[i]开头的最大子数组的结尾是第几位
    • 情况1:结尾相同
      • R e c [ i ] = R e c [ i + 1 ] Rec[i]=Rec[i+1] Rec[i]=Rec[i+1]
    • 情况2:结尾不同
      • R e c [ i ] = i Rec[i]=i Rec[i]=i
5.4.2 输出最优方案
  • 从子问题中查找最优解
  • 最大子数组开头位置 i i i,结尾位置 R e c [ i ] Rec[i] Rec[i]

6. 伪代码

Max-Continuous-Subarray-DP(X, n)

输入:数组X,数组长度n

输出:最大子数组和S_max,子数组起止位置l,r

// 初始化
D[n] ← X[n]
Rec[n] ← n
// 动态规划
for i ← n-1 to 1 do
	if D[i+1] > 0 then
		D[i] ← X[i] + D[i+1]
		Rec[i] ← Rec[i+1]
	end
	else
		D[i] ← X[i]
		Rec[i] ← i
	end
end
// 查找解
S_max ← D[1]
for i ← 2 to n do
	if S_max < D[i] then
		S_max ← D[i]
		l ← i
		r ← Rec[i]
	end
end
return S_max, l, r

​ 该算法的时间复杂度是 O ( n ) O(n) O(n)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值