動態規畫

      在〈動態規畫〉中尚無留言

網路上那麼多的動態規化教學,總是一大堆看不懂的東西,本篇先使用付款實例,進行不同的方式說明

付款實例

台灣的幣別有 1, 5, 10, 50, 100, 500, 1000 等 7 種幣別。

如果你消費了 987 元,那應該怎麼付錢,才能淘出最少數量的貨幣數量付給店家足夠的金額 ? 我們腦袋隨便一算,很容易的就知道要付 1 張 500 元,4 張 100 元,1 個 50元,3 個 10 元,1 個 5 元,2 個 1 元,總共是 1+4+1+3+1+2 = 12 個貨幣。

如果我們的貨幣 (狀態) 有上百種,那該如何用電腦算出最少的數量呢 ????
請特別注意,我們的要求,是如何付出最少的貨幣數量,當然金額一定要足夠。

思考邏輯

假設我們要付987元,那麼可以有如下選擇方案 — (付1元剩 986元),(付5元剩 982元),(付10元剩 977元),(付50元剩 937元),(付100元剩 887元),(付500元剩 487元),共六種方式。每一種付款方式,剩餘的金額分別為 (986, 982, 977, 937, 887, 487) ,而這些錢的付款最少次數以前就算過了(都是11次)。

所以就是(12,12,12,12,12,12)次,然後從這個集合中選取最小值。

上述 (986,982,977,937,887,487)  這些付款方式的最佳解,怎麼就以前就解過了呢? 原來是用 for 迴圈,從1塊錢,2塊錢,一直堆積上來,並儲存在記憶体中的。

總付款 : 987元
貨幣(一次) 剩餘 總共
1 986(11次) 11+1=12次
5 982(11次) 11+1=12次
10 977(11次) 11+1=12次
50 937(11次) 11+1=12次
100 887(11次) 11+1=12次
500 487(11次) 11+1=12次

實際代碼

底下是實際的完整代碼,最後顯示的結果是 12 種。

import collections
import math
def pay(coins, amount):
    dp = collections.defaultdict(lambda: float("inf"))
    dp[0] = 0
    for i in range(1, amount + 1):
        print(f'amount : {i} => {[1 + dp[i - coin] for coin in coins]} =>', end="")
        dp[i] = min([1 + dp[i - coin] for coin in coins])
        print(dp[i])
    return -1 if math.isinf(dp[amount]) else dp[amount]
print(pay([1,5, 10, 50,100,500,1000], 987))
結果 :
amount : 985 => [18, 14, 14, 14, 14, inf] =>14
amount : 986 => [15, 15, 15, 15, 15, inf] =>15
amount : 987 => [16, 16, 16, 16, 16, inf] =>12
12

動態規畫,就是從舊有經驗所取得的最佳解,然後獲取未來的解法。

方格移動

在一個 m*n 的格子中,從左上角 (0, 0) 走到右下角 (m-1, n-1) 共有幾種方式呢。這邊有個規定,只能往右邊或往下面走,不可以斜著走,也不可以往上往左走。

上圖要走到黃色那格(1,1),可能由 (0, 1) 往下走,也可能由(1, 0)往右走,所以要走到黃色格有2種方式。那要走到藍色那格有幾種方式呢,就是 (1, 1) 的2種方式+ (0,2) 的 1 種方式 = 3 種方式。

如此依序往右往下計算每一個,直到最右下角就是70種方式。

import numpy as np
def paths(m, n):
    # 先開一個 m*n 的大表格,並讓邊界的預設值為 1(代表有一種走法)。
    dp=np.ones([m,n], dtype=np.int32)
    for row in range(1, m):
        for col in range(1, n):
            dp[row][col] = dp[row-1][col] + dp[row][col-1]
    return dp[m-1][n-1]
print(paths(5,5))
結果 :
70

最大正方型面積

在一個 m*n 的格子中,1表示土地,0表示海洋,那麼如何計算最大的正方型土地面積呢? 下圖是矩陣的型狀。

>

底下是完整的程式碼

def maximalSquare(matrix):
    #map會根據指定的函數,對每個list裏的元素進行運算
    #比如 map(lambda x:x**x, [1,2,3,4])
    arr = [list(map(int, row)) for row in matrix]
    print(arr)
    for i in range(1, len(arr)):
        for j in range(1, len(arr[i])):
            arr[i][j] = min(arr[i-1][j], arr[i][j-1], arr[i-1][j-1]) + 1 if arr[i][j] == 1 else 0
    print(arr)
    return max([max(row) for row in arr], default=0) ** 2
matrix = [["1","0","1","0","0"],["1","0","1","1","1"],["1","1","1","1","1"],["1","0","0","1","0"]]
print(maximalSquare(matrix))
結果 :
[[1, 0, 1, 0, 0], [1, 0, 1, 1, 1], [1, 1, 1, 1, 1], [1, 0, 0, 1, 0]]
[[1, 0, 1, 0, 0], [1, 0, 1, 1, 1], [1, 1, 1, 2, 2], [1, 0, 0, 1, 0]]
4

動態規畫其實是一門很深的課程,差不多需一學期(6個月)的時間來學習,裏面有很多的演算法可以應用。有興趣的人可以參考如下卡卡恩大神所寫的文章.

https://ithelp.ithome.com.tw/articles/10215363

 

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *