- 类似题型:
动态规范总体思想
- 基于 斐波那契数列
f i b ( n ) = { 1 n = 1 或 n = 2 f i b ( n − 1 ) + f i b ( n − 2 ) o / w fib(n)=\begin{cases} 1 & n=1 或 n=2 \\ fib(n-1) + fib(n-2) & o/w \\ \end{cases} fib(n)={1fib(n−1)+fib(n−2)n=1或n=2o/w
- 动态规划解决来递归的重复子问题,递归空间时间复杂度
O
(
n
2
)
O(n^2)
O(n2) ,动态规划
O
(
n
)
O(n)
O(n)
- 比如说 下面的 例题二 中通过数组保存过程状态,需要用的时候直接获取到,不需要在算一遍
做题思路
- 选择第 i 个数字
- 不选 第 i 个数字
- 找出口
例题一
在数组中找出数字和最大的数,其中两个数不能是相邻的两个数
示例
arr = [1,2,4,1,7,8,3]
返回最大数为 15 [1,4,8]
方法一 动态规划
解题思路
- 如图 思路分析
- 先为数组添加下标 方便理解
- 根据条件可知 只能找不相邻的两个数
- 假设
opt[i]
表示到下标为 i 的最佳方案 - 选择下标为 i 的方案 opt[i-2] + arr[i]
- 不选择下标为 i 的方案 opt[i]
- 在比较 这两个的大小
- 找出口
1.当数组arr
只有一个数字的时候 最大数opt[0] = arr[0]
2.当数组arr
有两个数字的时候 最大数opt[1] = max(arr[0],arr[1])
- 根据思路可推到出公式
o p t ( 0 ) = { a r r [ 0 ] i = 0 opt(0)=\begin{cases} arr[0] & i = 0 \\ \end{cases} opt(0)={arr[0]i=0
o p t ( i ) = m a x = { o p t [ i − 2 ] + a r r [ i ] 选 择 o p t [ i − 1 ] 不 选 opt(i)=max=\begin{cases} opt[i-2]+ arr[i] & 选择 \\ opt[i-1] & 不选 \\ \end{cases} opt(i)=max={opt[i−2]+arr[i]opt[i−1]选择不选
动态规划写法
def dp_opt(arr):
opt = np.zeros(len(arr))
opt[0] = arr[0]
opt[1] = max(arr[0],arr[1])
for i in range(2,len(arr)):
a = opt[i-2] + arr[i]#选择arr[i]
b = opt[i-1]
opt[i] = max(a,b)
return opt[len(arr)-1]
递归写法
- 需要把 arr 传递进去
def rec_opt(arr,i):
# 找出口
if i ==0:
return arr[0]
elif i==1:
return max(arr[0],arr[1])
else:
a = rec_opt(arr,i-2) + arr[i] #选择arr[i]
b = rec_opt(arr,i-1)# 不选arr[i]
return max(a,b)
例题二
在数组中找出和为s的数,有则返回 True
,没有则返回 False
示例
arr = [3,34,4,12,5,2]
k = 9
结果 为True (4+5=9)
方法一 动态规划
解题思路
- 如图 思路分析
- 先为数组添加下标 方便理解
- 根据条件可知 需要找出和为 s 的数
- 我们假设
subset(i,s)
i 表示数组下标 s 表示需要求的数 - opt[i] 表示到下标为 i 的最佳方案
- 选择下标为 i 的方案 那么剩下的数组需要 subset(arr[i-1],s-arr[i])
- 不选择下标为 i 的方案 那么剩下的数组需要 subset[i-1] + arr[i]
- 在比较 这两个的大小
- 找出口
1.当s = 0
表示i
之前的数字加起来已经等于s
了 则直接返回True
2.当i = 0
的时候,表示arr[0] == s
才能返回为True
3.当左边的数字比右边的s
还有大,则(s-arr[i]<0)
,所以只能选择第二种情况,subset[i-1] + arr[i]
- 根据思路可推到出公式
s
u
b
s
e
t
(
i
,
s
)
=
{
r
e
t
u
r
n
T
r
u
e
s
=
=
0
a
r
r
[
0
]
=
=
s
i
=
=
0
s
u
b
s
e
t
(
i
−
1
,
s
)
a
r
r
[
i
]
>
s
s
u
b
s
e
t
(
i
−
1
,
s
−
a
r
r
[
i
]
)
a
r
r
[
i
]
<
s
且
选
择
i
s
u
b
s
e
t
(
i
−
1
,
s
)
a
r
r
[
i
]
<
s
不
选
择
i
subset(i,s)=\begin{cases} return True & s==0 \\ arr[0]==s & i==0 \\ subset(i-1,s) & arr[i]>s \\ subset(i-1,s-arr[i]) & arr[i]<s 且 选择 i \\ subset(i-1,s) & arr[i]<s 不选择 i\\ \end{cases}
subset(i,s)=⎩⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎧returnTruearr[0]==ssubset(i−1,s)subset(i−1,s−arr[i])subset(i−1,s)s==0i==0arr[i]>sarr[i]<s且选择iarr[i]<s不选择i
动态规划写法
- 需要使用二维数组来保存中间所有的动态过程
- 通常使用二维数组 ,都会把二维数组的下标分别加 1
- 返回 二维数组的最后一个数组
import numpy as np
def dp_subset(arr,S):
subset = np.zeros((len(arr),S+1),dtype = bool)
subset[:,0] = True
subset[0,:] = False
subset[0,arr[0]] = True
for i in range(1,len(arr)):
for s in range(1,S+1):
if arr[i]>s:
subset[i,s] = subset[i-1,s]
else:
a = subset[i-1,s-arr[i]] #选择arr[i]
b = subset[i-1,s] # 不选arr[i]
subset[i,s] = a or b
r ,c = subset.shape
return subset[r-1,c-1]
递归写法
- 需要把 arr 传递进去
arr = [3,34,4,12,5,2]
# 递归写法
def rec_subset(arr,i,s):
# 找出口
if s ==0:
return True
if i ==0:
return arr[0]==s
if arr[i]>s:
return rec_subset(arr,i-1,s)
else:
a = rec_subset(arr,i-1,s-arr[i]) #选择arr[i]
b = rec_subset(arr,i-1,s) # 不选arr[i]
return a or b