【动态规划】#1 最优回报

 题目:最佳回报方案

横轴为任务开始和结束的时间段,红字为任务回报价值,灰条左上角黑色数字为任务编号。

要求找出回报最大的安排。  

(截图来自灯神动态规划视频,侵删) 


思路与假设:  

假设:  
对于每项任务(i),分别考虑做与不做的最佳回报,再选出更好的方案。  
在决定最佳方案时,只关注任务(i)前面时间最近的任务哪个能做。   

思路:

如果一定要做i任务,那么前面的任务可以做哪个(我们称之为prev)    
对于任务i,opt()逻辑如下:
 ① 选它的最好的可能回报是 task[i][1] + opt(prev(i))  
 ② 不选它的最好的可能回报是 opt(i-1)  
 以上两个方案取大的数字作为最佳方案opt(i)的返回值  
 (题目范例有一定规律,每项任务的结束时间有序递增,且各方案的收益没有刚好相等情况。是否可能还有极端情况,例如:如果两个方案收益相等,这时可以选任务数量少的;如果任务数量也相等,选时间早的。此处暂不做展开。)  
 在题目条件下,i∈(0,9),opt(0)=0, opt(1)=5 

 (图片取自灯神dp讲解视频,侵删)  


代码实现:

def Solution(task):
    # part 1 定义prev
    prev = [0]*(len(task)+1)
    # 逐个元素计算最近可做任务prev,并存储在数组中
    for i in range(1,len(task)+1):
        for j in range(1,i-1):
            '''
            判断条件:时间衔接。
            此处借用了范例特征,任务时间最近时会首尾相连
            '''
            if(task[j-1][3]==task[i-1][2]):
                prev[i]=task[j-1][0]
    #part 2 定义 opt 与 tn,初始化出口元素 opt[1] tn[1]
    opt = [0]*(len(task)+1)
    tn = [[] for i in range(len(task)+1)]
    opt[1]=5
    tn[1]=[1]
    # 逐个元素检查最优回报和对应方案,并存储在数组中
    for i in range(2,len(task)+1):
        choose=task[i-1][1]+opt[prev[i]]
        no_choose=opt[i-1]
        if (choose>no_choose) :
            opt[i]=choose
            tn[i]=tn[prev[i]]+[i]
        else:
            opt[i]=no_choose
            tn[i]=tn[i-1]
    # 最后返回最优回报,以及对应的方案
    return max(opt), tn[opt.index(max(opt))]

arr = ([1,5,1,4],[2,1,3,5],[3,8,0,6],[4,4,4,7],[5,6,3,8],[6,3,5,9],[7,2,6,10],[8,4,8,11])
Solution(arr)

 返回结果:

(13, [1, 4, 8])

要点总结:

① 拆解思路:对于每个任务进行“做”与“不做”的比较,两种方案各有对应的递归逻辑
② 针对重复子问题,自下而上计算,递归变成递推,可降低复杂度,提高执行效率
③ 具体做法是用数组index来表示任务序号,把已求出的最优解按序号存放在数组中备用
④ 定义每个“递推/递归”逻辑时,理清出口条件,一般就是存储序列的第0个、第1个元素的值

实现过程中新学的姿势:

① 反向控制循环条件 for i in range(len(task),1,-1)
② 初始化数组 prev = [0]*(len(task)+1), opt = [0]*(len(task)+1)
③ 初始化二维数组 taskNo=[[] for i in range(len(task)+1)]
④ 数组元素添加 自增taskNo[i].append(i) 或者调取元素相加 taskNo[prev[i]]+[i] 
⑤ 返回数组最后一个元素 return opt[-1],taskDo[-1] 
⑥ 查找数组最大值 max(opt)
⑦ 返回数组中某值对应的索引号 opt.index(value)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值