[算法] 動態規划 (1) (工作最優收入)


一、問題

 

 

圖中的8個灰色在柱狀分別表示八份工作。所跨的寬度為做完該工作需要花費的時間。柱體中的紅色手寫數字為做完該份工作所能掙到的錢。

假設我們不能同時做有時間沖突的工作,問在0-11這個時間范圍內,我們最多能掙多少錢?如何安排做哪些工作賺的錢最多。

二、分析問題

這是一個典型的可以使用動態規划(DP)來解決的問題。

1.找出遞歸規律

首先,我們假設我們一定要做第8份工作的時候,由於一定要做第8份工作,那么由於第6、7份工作與第8份工作有時間沖突,所以6、7不能做。

那么,我們在決定是否選擇做第8份工作的時候,假設最大值為OPT(8)。

所以得到以下公式:

 

 

1)如果選擇做第8份工作,則OPT(8) = val(8) + OPT(5)   (因為一定要做8的話,前面最多能做到第5份工作)

2)如果不選擇做第8份工作,則OPT(8) = OPT(7)    (OPT(7)表示是否選擇做7的最大收入)

3)我們在選與不選之間取他們的最大值,最終得到真正的OPT(8)

2.寫出遞歸式

 

 

公式中的prev(i)就是一定要做第i份工作時,前面最近能做的某份工作的編號。例如prev(8) = 5。

prev是我們能夠直接通過題目計算出來的:

 

 

有了prev表,我們的OPT就能夠全部算出來了。

3.拆分計算過程

 

 

從圖中可以看到,我們在求各個OPT的時候,左右分支上會出現Overlap Subproblem(重復子問題)。這個結構和斐波那契數列的遞歸求解是一樣的。時間復雜度是O(2^n)。所以我們可以在計算過程中將這些子問題的結果記錄下來(利用列表等)。

4.計算結果

 

 

這樣的話,我們求解OPT的時間復雜度就變成了O(n)。

我們計算的過程中,還可以將每一步的最優選擇記錄下來:

 

 

 5.Python實現代碼

import numpy as np


def earnMore(data):
    data_len = len(data)
    # prev記錄如果要做某個工作(index),前面最近的不沖突的工作的編號
    prev_list = np.zeros(9, dtype=int)
    # 是否選擇某份工作(index)時,能夠達到的最大收益
    opt_list = np.zeros(9, dtype=int)
    # 首先計算各個工作對應的prev,從8開始算
    for i in range(len(data) - 1, 0, -1):
        start_time = data[i]['time'][0]
        for k in range(i - 1, 0, -1):
            end_time = data[k]['time'][1]
            if end_time <= start_time:
                prev_list[i] = k
                break

    # 從1開始計算OPT,記錄到opt_list中
    for i in range(1, len(data)):
        if i == 1:
            opt_list[i] = data[i]['val']
            continue
        if prev_list[i] == 0:
            opt_list[i] = max(data[i]['val'], opt_list[i - 1])
        else:
            opt_list[i] = max(data[i]['val'] + opt_list[prev_list[i]], opt_list[i - 1])

    return opt_list


if __name__ == '__main__':
    data = [
        {},
        {'val': 5, 'time': [1, 4]},
        {'val': 1, 'time': [3, 5]},
        {'val': 8, 'time': [0, 6]},
        {'val': 4, 'time': [4, 7]},
        {'val': 6, 'time': [3, 8]},
        {'val': 3, 'time': [5, 9]},
        {'val': 2, 'time': [6, 10]},
        {'val': 4, 'time': [8, 11]}
    ]
    res = earnMore(data)
    print("規定時間內,所能達到的最大收入為",res[-1])

 

##


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM