1. 程式人生 > >幾道動態規劃題目

幾道動態規劃題目

幾道常見的動態規劃題

通常暴力窮舉的方式是一種糟糕的策略,動態規劃正是一種解決類似問題的思想,如果一個問題滿足最優子結構,就可以通過把原問題可以分解為幾個子問題來解決,即全域性的最優解一定是某個區域性的最優解,我們需要一張表來儲存前一次計算的結果,以便遞推出原問題的解,這樣可以避免重複計算。

0-1揹包

固定容量的揹包,希望能夠裝入價值最大的物品。假設 m ( i ,

j ) m(i,j) 是揹包容量為 j j ,可選擇物品為0~i時0-1 揹包問題的最優值。 w
i w_i
為第i個物品的容量, v i v_i 為第i個物品的價值。

  • 當i=0時,表示沒有可選擇的物品,總價值為0。
  • 當j=0時,表示揹包容量為0,不好裝東西,總價值為0。
  • j w i j\geq w_i 時,可以選擇放入物品i或不放入物品i,若不放入物品i,則只剩下i-1個物品可以選擇,揹包容量仍為j,若放入物品i,也只剩下i-1個物品可供選擇,揹包剩餘容量為 j w i j-w_i
  • 0 j < w i 0 \leq j < w_i 時,揹包放不下物品i,只能在剩餘i-1個物品中選擇。

遞推式:
m ( i , j ) = { 0 i f   i = 0   o r   j = 0 max { m ( i 1 , j ) , m ( i 1 , j w i ) + v i } i f   j w i m ( i 1 , j ) i f   0 j < w i m(i,j)=\left\{\begin{matrix} 0 & if \ i=0 \ or \ j=0 \\ \max \{m(i-1, j), m(i-1, j-w_i)+v_i\} & if \ j \geq w_i\\ m(i-1,j) & if \ 0 \leq j < w_i \end{matrix}\right.

程式碼:

import numpy as np

def knapsack(c, w, v):
    assert len(w)==len(v)
    m = np.full((len(w) + 1, c + 1), -1)
    m[0,:] = 0  # i=0
    m[:, 0] = 0 # j=0
    for i in range(1, len(w) + 1):
        for j in range(1, c + 1):
            if w[i - 1] > j:
                m[i, j] = m[i-1, j]
            else:
                m[i, j] = max(m[i-1, j], m[i-1, j-w[i - 1]] + v[i - 1])
    print(m)
    # solution
    j = c
    x = np.full(len(w), -1)
    for i in range(len(w)):
        if m[i+1, j] == m[i, j]:
            x[i] = 0
        else:
            x[i] = 1
            j = j - w[i]
    return x
c = 10  # 揹包容量
w = [2,2,6,5,4]  # 物品容量
v = [6,3,5,4,6]  # 物品價值
s = knapsack(c, w, v)
print(s)
[[ 0  0  0  0  0  0  0  0  0  0  0]
 [ 0  0  6  6  6  6  6  6  6  6  6]
 [ 0  0  6  6  9  9  9  9  9  9  9]
 [ 0  0  6  6  9  9  9  9 11 11 14]
 [ 0  0  6  6  9  9  9 10 11 13 14]
 [ 0  0  6  6  9  9 12 12 15 15 15]]
[1 1 0 0 1]

可見,最大價值為15,選擇第0個、第1個以及第4個物品。

最小編輯距離

給出兩個單詞source和target,求出刪除、替換或插入某個字元使得source變為target的最少次數。
例如:source=‘intention’, target=‘execution’,最少次數為5,總共有5步。

  • intention -> inention (刪除 ‘t’)
  • inention -> enention (將 ‘i’ 替換為 ‘e’)
  • enention -> exention (將 ‘n’ 替換為 ‘x’)
  • exention -> exection (將 ‘n’ 替換為 ‘c’)
  • exection -> execution (插入 ‘u’)

假設 e d i t [ i , j ] edit[i,j] 為source的第0到i個字元與target的第0到j個字元分別組成的字串的最小編輯距離。

  •   i = 0   a n d   j = 0 \ i=0 \ and \ j=0 時,兩個都為空串,距離為0。
  •   i = 0   a n d   j > 0 \ i=0 \ and \ j>0 時,插入j次就行,距離為j。
  •   j = 0   a n d   i > 0 \ j=0 \ and \ i>0 時,刪除i次就行,距離為i。
  •   i 1   a n d   j 1 \ i\geq 1 \ and \ j\geq1 時,看是否新增source的第i個字元或刪除target的第j個字元能否使得剩餘部分相同,即繼續計算 e d i t [ i 1 , j ] edit[i-1,j] e d i t [ i , j 1 ] edit[i,j-1] ,如果新增或刪除後還不等,則需要繼續計算 e d i t [ i 1 , j 1 ] edit[i-1,j-1] ,我們取最小的情況。

遞推式:
e d i t [ i , j ] = { 0 i f   i = 0   a n d   j = 0 j i f   i = 0   a n d   j > 0 i i f   j = 0   a n d   i > 0 min { e d i t [ i 1 , j ] + i n s c o s t ( s o u r c e i ) , e d i t [ i 1 , j 1 ] + s u b s t c o s t ( s o u r c e i , t a r g e t j ) , e d i t [ i , j 1 ] + d e l c o s t ( t a r g e t j ) ) } i f   i 1   a n d   j 1 edit[i,j]=\left\{\begin{matrix} 0 & if \ i=0 \ and \ j=0\\ j & if \ i=0 \ and \ j>0\\ i & if \ j=0 \ and \ i>0\\ \min\{edit[i-1,j]+inscost(source_i),\\edit[i-1,j-1]+substcost(source_i,target_j),\\edit[i,j-1]+delcost(target_j))\} & if \ i\geq 1 \ and \ j\geq1 \end{matrix}\right.