动态规划之钢条切割问题——Rod-cutting problem

问题描述

问题的最优解包含相关子问题的最优解,这些子问题我们可以独立求解。

应用以下公式:

方法1:暴力法

长度为n的钢条可以有2^(n-1)中切割方案,因为离钢条左端距离分别为1,2,……,(n-1)的位置都有切或不切(0或1)两种选择,

但是我认为有好多重复的情况,假设切割的位置存储在数组中,那么[1]和[n-1]其实是一样的,因为都代表切成长为1和(n-1)的两段。但是具体重复情况(类似回文数的数量)有多少我一下子不知道怎么计算,但是数量级还是指数级的!!!

以下的图中伪代码基本相当于python代码,重大区别就是数组下标 i 有些差别!!!在我写的代码中

length = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
profit = [1, 5, 8, 9, 10, 17, 17, 20, 24, 30]

def cut_rod(p, n):
    if n == 0:
        return 0
    q = -1
    for i in range(n):
        q = max(q, p[i]+cut_rod(p, n-i))
    return q

print(cut_rod(profit, 9))

运行结果:

C:\Users\liuxiang15\Desktop\homework3>python rod_cutting.py
Traceback (most recent call last):
  File "rod_cutting.py", line 11, in <module>
    print(cut_rod(profit, 9))
  File "rod_cutting.py", line 9, in cut_rod
    q = max(q, p[i]+cut_rod(p, n-i))
  File "rod_cutting.py", line 9, in cut_rod
    q = max(q, p[i]+cut_rod(p, n-i))
  File "rod_cutting.py", line 9, in cut_rod
    q = max(q, p[i]+cut_rod(p, n-i))
  [Previous line repeated 993 more times]
  File "rod_cutting.py", line 8, in cut_rod
    for i in range(n):
RecursionError: maximum recursion depth exceeded in comparison

方法2:带备忘的自顶向下法

为了避免“自顶向下递归算法”中出现的重复计算重叠子问题的情况,用数组r存储不同长度下的最大效益

                 

代码:

length = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
profit = [1, 5, 8, 9, 10, 17, 17, 20, 24, 30]

def memorized_cut_rod(p, n):
    r = [-1]*(n+1)
    return memorized_cut_rod_aux(p, n, r)

def memorized_cut_rod_aux(p, n, r):
    if r[n] > 0:
        return r[n]
    q = -1
    if n == 0:
        q = 0
    else:
        for i in range(1, n+1):
            q = max(q, p[i-1]+memorized_cut_rod_aux(p, n-i, r))
    r[n] = q
    return q

for i in range(1, 11):
    print(memorized_cut_rod(profit, i))

运行结果:

C:\Users\liuxiang15\Desktop\homework3>python rod_cutting.py
1
5
8
10
13
17
18
22
25
30

方法3:自底向上法

length = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
profit = [1, 5, 8, 9, 10, 17, 17, 20, 24, 30]

def bottom_up_cut_rod(p, n):
    r = [0]*(n+1)
    for j in range(1, n+1):
        q = -1
        for i in range(1,j+1):
            q = max(q, p[i-1]+r[j-i])
        r[j] = q
    return r[n]  

for i in range(1, 11):
    print(bottom_up_cut_rod(profit, i))
C:\Users\liuxiang15\Desktop\homework3>python rod_cutting.py
1
5
8
10
13
17
18
22
25
30

方法4:扩展的自底向上法(s数组存储切割位置)

代码增加了一个功能,待切割长度n可以大于len(profit)

length = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
profit = [1, 5, 8, 9, 10, 17, 17, 20, 24, 30]

def extended_bottom_up_cut_rod(p, n):
    r = [0]*(n+1)
    s = [0]*(n+1)
    for j in range(1, n+1):
        q = -1
        for i in range(1,j+1):
            if i <= len(profit):
                if q < p[i-1]+r[j-i]:
                    q = p[i-1]+r[j-i]
                    s[j] = i
            else:
                if q < r[i]+r[j-i]:
                    q = r[i] + r[j-i]
                    s[j] = i
        r[j] = q
    # return r[n],s[n]
    return r,s
print(extended_bottom_up_cut_rod(profit, 15))
C:\Users\liuxiang15\Desktop\homework3>python rod_cutting.py
([0, 1, 5, 8, 10, 13, 17, 18, 22, 25, 30, 31, 35, 38, 40, 43], [0, 1, 2, 3, 2, 2, 6, 1, 2, 3, 10, 1, 2, 3, 2, 2])

方法4.1:使用方法4函数来存储切割段长在数组中

# method of how to cut the rod
def cut_rod_solution(p, n):
    (r,s) = extended_bottom_up_cut_rod(p, n)
    print(r)
    print(s)
    cut_method = []
    while n > 0:
        cut_method.append(s[n])
        # print(s[n])
        n -= s[n]
    return cut_method

print(cut_rod_solution(profit, 15))
C:\Users\liuxiang15\Desktop\homework3>python rod_cutting.py
[0, 1, 5, 8, 10, 13, 17, 18, 22, 25, 30, 31, 35, 38, 40, 43]
[0, 1, 2, 3, 2, 2, 6, 1, 2, 3, 10, 1, 2, 3, 2, 2]
[2, 3, 10]

不足:

没有对复杂度的计算分析,稍后补充!

参考博客

https://blog.csdn.net/yangfengyougu/article/details/82843134

https://blog.csdn.net/u010183397/article/details/46933639

https://www.cnblogs.com/tgycoder/p/4980655.html

https://segmentfault.com/a/1190000015150097?utm_source=tag-newest

欢迎大家批评指正!!!

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值