Approach 1: Bottom up constant space
DP定义,状态转移方程和初始化
-
DP[0][i]: maximum subarray ending onarr[i]. 也就是一定不删. 状态转移方程如下: $$ \begin{align} \rm{}DP[0][i] &= max(DP[0][i-1] + arr[i],arr[i])\ \rm{}DP[0][0] &= arr[0] \end{align} $$ 这个很好理解,没什么可说的,就是maximum subarray中的定义 -
DP[1][i]: maximum subarray ending onarr[i], but have a right to delete last elementarr[i].也就是你可以删,也可以不删,但你如果删必须删除最后一个, 也就是arr[i]. 那么状态转移方程如下 $$ \begin{align} \rm{DP}[1][i] &= max(\rm{DP}[1][i-1] + arr[i],arr[i],\rm{DP}[0][i-1])\
\rm{}DP[1][0] &= arr[0]
\end{align}
$$
这个理解比较费脑筋,但我来解释一下,
- DP[1][i-1] + arr[i]: 我删除前面的任何一个点了.
- arr[i] : 删掉前面全部的了,由于DP[1][i-1]为负数,只是累赘.
- DP[0][i-1]: reset前面的删除,我现在碰到负数的global min了,我要删arr[i]了!
code
class Solution:
def maximumSum(self, arr: List[int]) -> int:
# DP[0][i]: maximum subarray ending on arr[i].
# DP[1][i]: maximum subarray ending on arr[i], optionally have the right to drop the arr[i].
# declare DP and initialization
DP = [[0 for _ in range(len(arr))] for _ in range(2)]
DP[0][0] = arr[0]
DP[1][0] = arr[0]
for i in range(1,len(arr)):
# kadane' algorithm
DP[0][i] = max(DP[0][i-1] + arr[i], arr[i])
# 可以舍弃arr[i]这一element
DP[1][i] = max(DP[1][i-1] + arr[i], arr[i], DP[0][i-1])
return max(DP[1])
Approach 2: Jerry's approach
Jerry定义的状态转移方程和我一模一样,但DP的定义和初始化有slight difference, 这个区别实际上就来自于初始化对于DP定义的影响.
DP定义需要: - 初始条件 - 状态转移方程
这两个缺一不可,看这个例子jerry定义的DP如下:
-
DP[0][i]: maximum subarray ending onarr[i]. 也就是一定不删. 状态转移方程如下: $$ \begin{align} \rm{}DP[0][i] &= max(DP[0][i-1] + arr[i],arr[i])\ \rm{}DP[0][0] &= arr[0] \end{align} $$ 这个和我的定义一样. -
DP[1][i]: maximum subarray ending onarr[i], but must delete the last elementarr[i].也就是你可以删,也可以不删,但你如果删必须删除最后一个, 也就是arr[i]. 那么状态转移方程如下 $$ \begin{align} \rm{DP}[1][i] &= max(\rm{DP}[1][i-1] + arr[i],arr[i],\rm{DP}[0][i-1])\
\rm{}DP[1][0] &= 0
\end{align}
$$
可以看到equation (8) and (4)是唯一的区别,但这个initial condition直接造成了:
- 我的定义: 可以删除,可以不删除, 但如果删除一定删除arr[i]
- Jerry's 定义: 一定删除一个点, no matter what.
Takehome is DP的定义不仅是state-transition function还和初始条件相关。
提醒了我们以后在设计DP时候,不仅仅要花时间和心思思考状态转移方程,初始状态也要花心思考虑. Jerry代码如下:
# Jerry's code
class Solution:
def maximumSum(self, arr: List[int]) -> int:
# DP[0][i]: maximum subarray ending on arr[i].
# DP[1][i]: maximum subarray ending on arr[i], optionally have the right to drop the arr[i].
if len(arr) == 1: return arr[0]
# declare DP and initialization
DP = [[0 for _ in range(len(arr))] for _ in range(2)]
DP[0][0] = arr[0]
DP[1][0] = 0
max_so_far = arr[0]
for i in range(1,len(arr)):
# 可以舍弃arr[i]这一element
DP[1][i] = max(DP[1][i-1] + arr[i], arr[i], DP[0][i-1])
DP[0][i] = max(DP[0][i-1] + arr[i], arr[i])
max_so_far = max(max_so_far,DP[0][i],DP[1][i])
# return max(max(DP[1]),max(DP[0]))
return max_so_far
重点看jerry的initial conditions, 虽然DP[1][0]是强制删除了第一个node的情况,但是max_so_far考虑了必须是非空subarray的前提条件
DP[0][0] = arr[0]
DP[1][0] = 0
max_so_far = arr[0]