Python算法入门day10——动态规划

分析找到递推式

存子问题

【钢条切割问题】

1、题目描述

某公司出售钢条,出售价格与钢条长度之间对关系如下表:

问题:现在有一段长度为n的钢条和上面的价格表,求切割钢条方案,使得总收益最大。

长度为4的钢条的所有切割方案如下:(c方案最优)

思考:长度为n的钢条的不同切割方案有几种?

答:长度为n就有2^(n-1)次切割方法

给出题目的最优表

现在就是需要从小到大的最优解算出来即可,即当要计算长度为4的最优解,可以观察前面的(1+3)的最优解是多少,(2+2)的最优解是多少,以及本身不切割的最优解是多少即可。

2、题目解析

 【递推式】

设长度为n的钢条切割后最优收益值为rn,可以得出递推式:

1.第一个参数Pn表示n自身,不切割

2.其他n-1个参数分别表示另外n-1种不同切割方案,对方案i取1,2……,n-1

        将钢条切割为长度为i和n-i两端

        方案i的收益为切割两段的最优收益之和

3.考察所有的i,选择其中收益最大的方案

【最优子结构】

1.可以将求解规模为n的原问题,划分为规模更小的子问题:完成一次切割后,可以将产生的两段钢条看成两个独立的钢条切割问题。

2.组合两个子问题的最优解,并在所有可能的两段切割中选取组合收益最大的,构成原问题的最优解。

3.钢条切割满足最优子结构:问题的最优解由相关子问题的最优解组合而成,这些子问题可以独立求解。

eg:假设长度为9的钢条长度

如果3,6是最优子结构,则长度为9的最优解是长度为3的子结构和长度为6的子结构相加。

【优化递推式】

钢条切割问题还存在更简单的递归求解方法

        1.从钢条的左边切割下长度为i的一段,只对右边剩下的一段继续进行切割,左边的不再切割

        2.递推式化简为:*只是在原来递推式的基础上将右边换成p[i].

        3.不做切割的方案就可以描述为:左边一段长度为n,收益为p[n],剩下一段长度为0,收益为r[0]=0.

eg:假设长度为9的钢条长度(假设切成2 3 2 2最优)

在原来的递推式里,可以看成5和4切割(5切成2和3,4切成2和2)所以5+4最优

在优化后的式子里,可以切成2和7 (2不切,表示左边的一段,只切右边的7,切成2 2 3)也是最优

 3、三种实现代码

#钢条长度的价格,下标表示多少长
p=[0,1,5,8,9,10,17,17,20,24,30]

#方法一:双递归
def cut_rod_recurision(p,n):
    if n==0:
        return 0
    else:
        res=p[n] #表示不切割的价值
        #遍历从1到n-1;(p[1]+p[n-1])...(p[n-1]+p[1])
        for i in range(1,n):
            res=max(res,cut_rod_recurision(p,i)+cut_rod_recurision(p,n-i))
    return res
    
#方法二:优化,单递归
def cut_rod_recurision_1(p,n):
    if n==0:
        return 0
    else:
        res=p[n]
        for i in range(1,n):
            #切成两部分,左边不动,右边切()
            '''
            设9的最优解是 2 3 2 2
            上面那种方法可以对 4,5分别切割
            这种方法则可以看成2 7,对7切割成2 2 3
            '''
            res=max(res,p[i]+cut_rod_recurision_1(p,n-i))
    return res

#方法三:动态规划(存放)
def cut_rod_recurision_dp(p,n):
    li=[0]#创建一个用于存放最优解的列表
    
    #i表示1~n的最优解
    for i in range(1,n+1):
        res=p[i]
        #用于计算每位i的最优解
        for j in range(1,i+1):
            #优化递推式
            res=max(res,p[j]+li[i-j])
        #将最大的i存入li中
        li.append(res)
    return li[n]
            

print(cut_rod_recurision(p,9)) #25
print(cut_rod_recurision_1(p,9)) #25
print(cut_rod_recurision_dp(p,9)) #25

4、 钢条切割问题——重构解

题目修改为不仅要输出最优解,还要输出最优切割方案

【解题思路】

在方法三的基础上增加一个s列表,用于存放1~n的最大价格的左边不切割长度

遍历循环,将输入的n减去s列表里的最优左长度,当n为0,程序结束。

def cut_rod_extend(p,n):
    r=[0] #用于存放最优结果 最大价格
    s=[0] #用于存放价格最大值对应方案的左边不切割部分的长度
    for i in range(1,n+1):
        res_r=0 #最优解
        res_s=0 #左半段不切割部分的长度
        for j in range(1,i+1):
            if p[j]+r[i-j]>res_r:
                res_r=p[j]+r[i-j]
                res_s=j
        r.append(res_r)
        s.append(res_s)
    return r[n],s

def cur_rod_solution(p,n):
    r,s=cut_rod_extend(p,n)
    res=[] #用于存放最优方案
    while n>0: #为0返回
        res.append(s[n]) #从s列表中找最优左长度
        n-=s[n] #输入的数减去最优左长度
    return r,res

print(cur_rod_solution(p,9))  #(25, [3, 6])     

5、动态规划问题关键特征

        1.什么问题可以使用动态规划方法?

        和最优值/最优化的问题相关(什么什么最大,什么最小)

                最优子结构

                        原问题的最优解中涉及到多少个子问题

                        在确定最优解使用哪些子问题时,需要考虑多少种选择

                重叠子问题

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值