动态规划例题

动态规划例题

背包九讲

求解非相邻数字的最大和

题目: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

未完待续

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值