1. 程式人生 > 資訊 >長沙比亞迪疑致多名兒童流鼻血,去年排汙抽查曾發現問題

長沙比亞迪疑致多名兒童流鼻血,去年排汙抽查曾發現問題


title: 鋼條切割問題
date: 2022-05-10 20:42:52
tags: 動態規劃

鋼條切割問題

問題背景

現在有一個長度為10的鋼條,可以零成本將其切割成多段長度更小的鋼條,我們先要求出最大收益

鋼條長度 0 1 2 3 4 5 6 7 8 9 10
價格p 0 1 5 8 9 10 17 17 20 24 24

如果我們不切割的話可以獲得的最大收益為 24

如果我們按照 2 2 6 切割方法,收益為 27

所以不同的切割方法收益不同,我們尋求的就是收益最大的切割方法

問題定義

輸入:

  • 鋼條的長度n
  • 價格表pl(1≤ l ≤n):表示長度為l的鋼條價格

輸出:

  • 求得一組切割方法,令收益最大化

問題觀察

  1. 假設鋼條能夠至多切割一次:有以下這幾種情況

    我們就需要從這幾種切割情況中尋找出收益最大的,max{p[i] + p[10-i], p[10]}

  2. 如果鋼條能夠切割兩次:

    我們可以現將鋼條切割出一段

    然後再剩餘的鋼條中繼續切割

    這時候 長度為8的就可以看做切割次數為一的那一種情況

​ 這裡可能存在最優子結構重疊子問題

問題結構分析

問題表示:

C[j]:切割長度為j的鋼條可得到的最大總收益

遞推關係的建立

C[j] = Max{p[i] + C[j-i], p[j]}

C[j] 表示從這j種情況中選出最大的哪一種

這裡面就存在最優子結構問題

自底向上的計算

我們通過 C[0] = 0

這種情況來推斷整個C陣列

以後C[j] 的求導需要 依託於 C[1] ~ C[j - i] 這麼多種情況中的最優解來進行C[j]的求解

所以這是一種區間性動態規劃,每走一步都要依賴於一個區間的最優值

最優方案追蹤

遞迴出口:輸出的鋼條長度為n

演算法例項

鋼條長度 0 1 2 3 4 5 6 7 8 9 10
價格p 0 1 5 8 9 10 17 17 20 24 24
  1. 我們先初始化C[0] = 0
i 0 1 2 3 4 5 6 7 8 9 10
C[i] 0
rec 0
  1. 鋼條長度為1

    這時候沒得選只能夠選擇1

i 1
1
i 0 1 2 3 4 5 6 7 8 9 10
C[i] 0 1
rec 0 1 2 3 4 5 6 7 8 9 10
1
  1. 鋼條長度為2:

​ 有兩種情況

  • 切割一刀:結果為p[1] + C[2-1] = 2
  • 不切割:結果為p[2] = 5

從上面選擇情況最大的:不切割

i 1 2
2 5
i 0 1 2 3 4 5 6 7 8 9 10
C[i] 0 1 5
rec 0 1 2 3 4 5 6 7 8 9 10
1 2
  1. 鋼條長度為3:

​ 有三種情況:

  • 在1那切割一刀:C[3] = p[1] + C[3-1] = 1 + C[2] = 6
  • 在2那切割一刀:C[3] = p[2] + C[3-2] = 5 + 1 = 6
  • 不切割:C[3] = p[3] = 8

從上面選擇情況最大的:不切割

i 1 2 3
6 6 8
i 0 1 2 3 4 5 6 7 8 9 10
C[i] 0 1 5 8
rec 0 1 2 3 4 5 6 7 8 9 10
1 2 3
  1. 鋼條長度為4:

​ 有四種情況:

  • 在一那切割一刀:C[4] = p[1] + C[3] = 1 + 8 = 9

  • 在二那切割一刀:C[4] = p[2] + C[2] = 5 + 5 = 10

  • 在三那切割一刀:C[4] = p[3] + C[1] = 8 + 1 = 9

  • 不切割:C[4] = p[4] = 9

    從上面選擇最大的哪一種情況:在二那切割一刀

    i 1 2 3 4
    9 10 9 9
    i 0 1 2 3 4 5 6 7 8 9 10
    C[i] 0 1 5 8 10
    rec 0 1 2 3 4 5 6 7 8 9 10
    1 2 3 2
  1. 剩下哪幾種情況不在一一列舉

演算法實現

'''
鋼條切割問題
這個演算法主要需要實現以下這幾種情況:
    1.C陣列(記錄權值)
    2.Rec陣列(追蹤效果)
    3.i陣列(記錄C[i]的各種情況的權重)
'''
def steel_bar_cutting(price_list, C, Rec):

    # 初始化i陣列
    I = []

    # 遍歷鋼條長度 從1-len(price_list)
    for length in range(1, len(price_list)):
        
        # 初始化最大值, 最大值下標
        max_data = 0
        max_index = 0

        # 尋找切割鋼條的 幾種情況, 並尋找最大的值,和最大值的下標
        for m in range(1, length+1):
            m_data = price_list[m] + C[length-m]
            I.append(m)

            if m_data > max_data:
                max_data = m_data
                max_index = m
        # 把上述幾種方案中最大值 賦值給C陣列對應的位置, 把切割位置賦值給對應的Rec位置
        C[length] = max_data
        Rec[length] = max_index

# 追蹤最優方案
def track(C, Rec):
    length = len(C) - 1
    i = length
    print(f"最大獲利為:{C[i]}")

    print("獲利方案為:")
    while(length > 0):
        print(f"在{Rec[i]}位置切一刀")
        i = length - Rec[i]
        length = i


#  初始化資料
price_list = [0,1,5,8,9,10,17,17,20,24,24]
C = [0 for i in range(len(price_list))]
Rec = [0 for i in range(len(price_list))]

steel_bar_cutting(price_list, C, Rec)
track(C, Rec)