算法小專欄:動態規劃(一)

級別: ★☆☆☆☆
標籤:「算法」「DP策略」「動態規劃」
做者: MrLiuQ
審校: QiShare團隊php


本篇將介紹動態規劃相關知識。html

1、簡介

動態規劃(Dynamic Programming,簡稱DP)。git

它的核心思想是把一個複雜的大問題拆成若干個子問題,經過解決子問題來逐步解決大問題github

注意:使用動態規劃思想有個前提:當且僅當每一個子問題都是離散的(即每一個子問題都不依賴於其餘子問題時),才能使用動態規劃。算法


2、動態規劃之「0-1揹包問題」

如今有這麼一個場景, 「你」是一名「小偷」,你帶了個包去「偷東西」,。數組

條件1:每一個商品只有一個,要麼拿,要麼不拿。(0-1揹包問題) 條件2:你最多拿得動4kg的東西。(固定大小,可不裝滿)微信

商品 價格 重量
商品A 3000元 4kg
商品B 2000元 3kg
商品C 1500元 1kg
商品D 2000元 1kg

有限的重量條件下,如何**「偷」**,賺的錢最多?spa

方案一:簡單算法(可行,不推薦)

暴力枚舉出全部商品的排列組合, 捨去全部超出重量要求的組合, 從中挑一個最大的。.net

可行,可是太慢了,每多一件商品都會多2倍的組合。3d

方案測評:時間複雜度 O(2n),超級超級慢,不推薦。

方案二:貪心算法(不可行)

用上篇介紹的貪心算法計算。

經過某個貪心策略(拿最貴的、拿性價比最高的商品)來得出近似解。

方案測評:這種方案接近最優解,是近似解,但不必定是最優解,故不可行。

方案三:動態規劃(可行,推薦)
  • 原理:先解決子揹包最優,再解決大揹包最優。

先繪製出一張表格,一會咱們一列一列慢慢填。(PS:體會動態規劃的算法過程)

表格:(實際上對應了一個二維數組)

商品\ 子揹包最大重量 1kg 2kg 3kg 4kg
商品A
商品B
商品C
商品D

先解讀一下這個表格, 行:表明了商品行(對應i), 列:表明了重量列(對應j), 格:表明當前的已有的商品、已有重量下所能拿的最大價值

好了,下面咱們開始一列一列的填:

第一行,只有商品A(價值:3000,重量:4kg)

商品\ 子揹包最大重量 1kg 2kg 3kg 4kg
商品A /
商品B
商品C
商品D

第二行,有商品A(價值:3000,重量:4kg)與商品B(價值:2000,重量:3kg)

商品\ 子揹包最大重量 1kg 2kg 3kg 4kg
商品A /
商品B /
商品C
商品D

第三行,有商品A(價值:3000,重量:4kg)、商品B(價值:2000,重量:3kg)商品C(價值:1500,重量:1kg)

商品\ 子揹包最大重量 1kg 2kg 3kg 4kg
商品A /
商品B /
商品C 1500
商品D

第四行,有商品A(價值:3000,重量:4kg)、商品B(價值:2000,重量:3kg)、商品C(價值:1500,重量:1kg)、商品D(價值:2000,重量:1kg)

商品\ 子揹包最大重量 1kg 2kg 3kg 4kg
商品A /
商品B /
商品C 1500
商品D 2000

你們有沒有發現,這裏填寫每一個表格時的算法可表示爲:

對應行的商品的重量超過當前子揹包的重量,就取上一行單元格的值, 商品的重量能裝下當前子揹包,則取下面二者的較大值:

  • 上一個單元格的值(cell[i-1][j]
  • 當前商品的價值 + 剩餘空間的價值(cell[i-1][j-當前商品的重量所對應的列號]

下面填第二列:

商品\ 子揹包最大重量 1kg 2kg 3kg 4kg
商品A / /
商品B / /
商品C 1500 1500
商品D 2000 3500

第三列:

商品\ 子揹包最大重量 1kg 2kg 3kg 4kg
商品A / / /
商品B / / 2000
商品C 1500 1500 2000
商品D 2000 3500 3500

第四列:

商品\ 子揹包最大重量 1kg 2kg 3kg 4kg
商品A / / / 3000
商品B / / 2000 3000
商品C 1500 1500 2000 3500
商品D 2000 3500 3500 4000

於此反覆判斷便可,這樣每一個單元格都是最優解,經過解決子問題,推導出最終最優解。 這就是動態規劃,是否是很簡單呢?

轉換成Python代碼:

def package_dp(a, b, flag, n):
    c = [[0 for i in range(n)] for j in range(n)]
    for j in range(n):
        c[0][j] = 0

    for i in range(n):
        c[i][0] = 0
        for j in range(n):
            if b[i]>flag[j]:
                c[i][j] = c[i-1][j]
            else:
                temp1 = a[i] + c[i-1][j-b[i]]
                temp2 = c[i-1][j]
                c[i][j] = max(temp1,temp2)
            print c[i][j]
        print ("")
    return c

price = [0, 3000, 2000, 1500, 2000]
weight = [0, 4, 3, 1, 1]
flag = [0, 1, 2, 3, 4]

package_dp(price, weight, flag, 5)
複製代碼

3、細節問題

  • 子揹包拆分問題:按照 全部商品 的最大公約數(也有可能存在小數)去拆子揹包。 讓全部的商品都能被恰好裝下。

  • 經過子揹包的最優解 => 推導出 => 全揹包的最優解。 這個過程的思想,就是DP思想(動態規劃的核心思想)


4、動態規劃的應用場景

本文舉了揹包與矩陣連乘的例子,其實思路都是同樣的。 只是應用場景不一樣,常見的應用場景有如下幾個:

  • 0-1揹包問題( ✔️)
  • 矩陣連乘法( ✔️)
  • 硬幣找零
  • 字符串類似度
  • 最長公共子序列
  • 最長遞增子序列
  • 最大連續子序列和/積
  • 有代價的最短路徑
  • 瓷磚覆蓋(狀態壓縮DP)
  • 工做量劃分

參考資料:


小編微信:可加並拉入《QiShare技術交流羣》。

關注咱們的途徑有:
QiShare(簡書)
QiShare(掘金)
QiShare(知乎)
QiShare(GitHub)
QiShare(CocoaChina)
QiShare(StackOverflow)
QiShare(微信公衆號)

推薦文章:
Dart基礎(一)
Dart基礎(二)
Dart基礎(三)
Dart基礎(四)
iOS 短信驗證碼倒計時按鈕
iOS 環境變量配置
iOS 中處理定時任務的經常使用方法
奇舞週刊

相關文章
相關標籤/搜索