动态规划例题
求解非相邻数字的最大和
题目:arr = [1,2,4,1,7,8,3],求其中非相邻数字可组成的最大和。
如本题解为15 =1+4+7+3
# =============================================================================
# 从一堆数字中,选出不相邻的数,相加和最大
# 做法:从最后一个开始,分别计算“选”该项与“不选”该项可获取的最大值opt(i)
# opt(i) = max{"选": Vi + opt(prev(i)), "不选": opt(i-1))}
# 其中prev(i)表示的是,当选择当前项时,只可选择的下一项,即选择下一个不相邻的数字位置
# i 从 1 到 n,逐个求解 opt(i),opt[-1]便为最后解
# =============================================================================
def function(data):
# 若只有一个或两个数字时
if len(data) == 1:
return data[0]
if len(data) == 2:
return max(data[0],data[1])
opt = [0] * len(data)
opt[0] = data[0]
opt[1] = max(data[0],data[1])
# 更新 opt 数组
for i in range(2,len(data)):
# 若 “选”
select = data[i] + opt[i-2]
# 若 “不选”
Noselect = opt[i-1]
opt[i] = select if select >= Noselect else Noselect
#opt:[1, 2, 5, 5, 12, 13, 15]
return opt[-1]
if __name__ == '__main__':
arr = [1,2,4,1,7,8,3] # 最大组合15 = 1,4,7,3
print(function(arr))
任务安排最大报酬化
选择任务,达到报酬最大化(5 + 4 + 4 = 13)
# =============================================================================
# 任务描述:
# 总共八个任务,给定每个任务的时间段(start,end),报酬price
# 规划返回能获得的最大报酬
# 做法:从最后一个开始,分别计算“选”该项与“不选”该项可获取的最大值opt(i)
# opt(i) = max{"选": Vi + opt(prev(i)), "不选": opt(i-1))}
# 其中prev(i)指的是 i 不影响的最接近的任务
# =============================================================================
# 递归的方式,会存在多次调用opt(5)的情况,即重叠子问题
# 该方法为从上往下的方式,即从opt(8)开始往下
def function(data):
index = len(data) - 1
# 当长度为1时,直接返回价值
if index == 0:
return data[index][2]
# 若“选”
preIndex = -1
# 计算prev(i)
for i in range(index-1,-1,-1):
#当前一任务的结束时间小于当前任务的开始时间,表示互不干扰
if data[i][1] <= data[index][0]:
preIndex = i
break
if(preIndex > -1): # 需判断是为-1,若为则无需继续递归
select = data[index][2] + function(data[:preIndex+1])
else:
select = data[index][2]
# 若 “不选”
Noselect = function(data[:index])
return select if select >= Noselect else Noselect
# 通过保存子问题解的方式,避免子问题重复求解
# 该方法变为从下到上,从opt(1)开始,逐个计算opt(i)直到opt(8)
def function2(data):
opt = [0]*len(data)
preIndex = [0]*len(data)
# 计算 preIndex,即 prev(i)
for i in range(len(data),0,-1):
for j in range(i-1,0,-1):
if(data[j-1][1] <= data[i-1][0]):
preIndex[i-1] = j
break
# # 从小到大计算opt(i)
for i in range(len(data)):
if i == 0 :
opt[i] = data[0][2]
continue
# 若“选”
if preIndex[i] == 0:
select = data[i][2]
else:
select = data[i][2] + opt[preIndex[i]-1]
# 若“不选”
Noselect = opt[i-1]
opt[i] = select if select >= Noselect else Noselect
return opt[-1]
if __name__ =='__main__':
all = [[1,4,5],[3,5,1],[0,6,8],[4,7,4],[3,8,6]
,[5,9,3],[6,10,2],[8,11,4]] # 最大报酬 13
print(function(all))
print(function2(all))
数组中是否可组成目标数字
# =============================================================================
# 判断一个数组arr中,若存在数字组合为给定值value,返回True,否则 False
# 从后往前,选当前数字arr[i],往下判断(arr[i-1],value - arr[i])是否为True
# bu选当前数字,往下判断(arr[i-1],value)是否为True
# 判断 (arr[i-1],value - arr[i]) or (arr[i-1],value)
# =============================================================================
# 递归方式,从上往下
def function(data,value):
# 出口条件
if value == 0: # 当value为 0 时,返回 True
return True
elif len(data) == 1: # 当只剩一个数字时
return data[0] == value
elif data[-1] > value: # 若当前数字大于value,只能”不选“
return function(data[:-1],value)
else: # 否则分别判断“选”与“不选”的情况
select = function(data[:-1], value - data[-1])
Noselect = function(data[:-1],value)
return select or Noselect
# 动态规划方式,从下往上
# 构建二维数组,len(data) * value(0-value)
# 每个位置标示,(arr[i],value)的结果,True or False
# 则最后一个位置为最终结果
import numpy as np
def function2(data,value):
matrix = np.zeros((len(data),value+1),dtype=bool)
# 当value为0时,该列全为True
matrix[:,0] = True
# 当只剩一个数字时,只有该数字等于value时为True
matrix[0,:] = False
matrix[0,data[0]] = True
# 进行递推
for i in range(1,len(data)):
for s in range(1,value+1):
if data[i] > s:# 若当前数字大于value,只能”不选“
matrix[i,s] = matrix[i-1,s]
else:
select = matrix[i-1, s - data[i]]
Noselect = matrix[i-1,s]
matrix[i,s] = select or Noselect
return matrix[-1,-1]
if __name__ == '__main__':
arr = [3,34,4,12,5,2]
s = 9
print(function2(arr,s))
# 注:无法处理负数 -1
01背包为题
# ============================================================
# 01背包问题
# 有n件物品,每件物品的重量为w[i],价值为c[i]。现有一个重量为 V 的背包,
# 问如何选取物品放入背包,使得背包内物品的总价值最大。其中每种物品只有 1 件
# 令dp[i][j]表示 挑选前 i 个物品时,背包容量为 j 情况下的最大价值
# 当 j < w[i] 时,表示背包放不下第 i 个物品,dp[i][j]=dp[i-1][j]
# 可放入时,计算拿进去与不拿进去两种情况下的价值,取最大值
# 拿进去 dp[i-1][j - w[i]] + c[i]
# 不拿进去 dp[i-1][j]
# ============================================================
def dp_way(w,c,V):
dp = [[0 for i in range(V+1)] for j in range(len(w)+1)]
for i in range(1,len(w)+1):
for j in range(V+1):
if j < w[i-1]:
dp[i][j] = dp[i-1][j]
else:
a = dp[i-1][j-w[i-1]]+c[i-1]
b = dp[i-1][j]
dp[i][j] = max(a, b)
return dp[-1][-1]
if __name__ == '__main__':
w = [1,2,3,4]
c = [30,20,10,40]
V = 5
print(dp_way(w,c,V))
# dp = [[0]*(V+1)]*(len(w)+1)
# 这种创建方式会导致dp[0][1]=30后,每行的第二列都为30
未完待续