1. 程式人生 > >動態規劃原理講解(下)

動態規劃原理講解(下)

本章節主要結合動態規劃的例項程式碼進行講解,使用python語言。

題目一: 選出的數字不能相鄰,且使得選出的數字總和最大。

1.1 問題原理介紹 

比如:可以選擇1和9,結果為10。

但是如果選擇4和9,結果為13,則結果為最優的。 

並且選擇的時候不一定非要選擇兩個,也可以選擇其它方案,如下圖所示

但是選出的方法結果也不會超過13,所以該方案並非最優。

1.2 程式碼與例項講解

上節我們採用的是選與不選的解決方法來解決動態規劃問題,對於該例項同樣適用。

例項內容解析:OPT(6)為求解到下標為6的位置的最佳方案是什麼?

分析OPT(6):(+)代表選擇,(-)代表不選。

當然這裡面有很多的重疊子問題:OPT(3)、OPT(4)

遞迴方程表示上述過程:

OPT\left ( 0 \right )=arr[0]

OPT\left ( 1 \right )=max\left\{\begin{matrix} arr[0]\\ arr[1] \end{matrix}\right.

程式程式碼:


In [6]:
arr=[1,2,4,1,7,8,3]
def rec_opt(arr,i):
    if i==0:
        return arr[0]
    elif i==1:
        return max(arr[0],arr[1])
    else:
        A=rec_opt(arr,i-2)+arr[i]
        B=rec_opt(arr,i-1)
        return max(A,B)
rec_opt(arr,6)

Out[6]:
15

存在的問題:上述程式碼存在重疊子問題,運算速度會很慢,時間複雜度會達到O\left(2^{n} \right ),採用非遞迴方法:

import numpy as np
arr=[1,2,4,1,7,8,3]

#非遞迴方法
def dp_opt(arr):
    opt=np.zeros(len(arr))
    opt[0]=arr[0]
    opt[1]=max(arr[0],arr[1])
    for i in range(2,len(arr)):
        A=opt[i-2]+arr[i]
        B=opt[i-1]
        opt[i]=max(A,B)
    return opt[len(arr)-1]
    
dp_opt(arr)

output:
15.0

題目二:從一個數組中選出一堆數字,使得這些數字的和等於給定的數字9,查詢是否存在這樣的方案?如果存在這樣的方案,則列印True,否則False。

假設:所有的數字都是正整數

思路與上題一致。

subset(i,s)考慮第i個數字選不選,s為要求的數字。

只要左右兩邊的任何一個方案成立,那麼就時True。中間用or連線。

出口設計:

(1)在處理到一半的時候s已經變為0。比如當我進行到arr[2]時,後面的已經能夠得到我想要的數字,則前面的已經沒有必要進行計算。直接輸出True。

(2)如果i=0,arr[0]=s時才能返回True。

(3)如果arr[i]>s,就只考慮不選arr[i]這個數字的情況。

遞迴過程:

 程式程式碼:

#三個出口條件,採用遞迴方法
aar=[3,34,4,12,5,2]
def rec_subset(arr,i,s):
    if s==0:
        return True
    elif i==0:
        return arr[0]==s
    elif arr[i]>s:
        return rec_subset(arr,i-1,s)
    else:
        A=rec_subset(arr,i-1,s-arr[i])
        B=rec_subset(arr,i-1,s)
        return A or B
    
print(rec_subset(arr,len(arr)-1,9))
print(rec_subset(arr,len(arr)-1,10))
print(rec_subset(arr,len(arr)-1,11))
print(rec_subset(arr,len(arr)-1,12))

output:
True
True
True
True

採用非遞迴的形式:

思路分析:

程式程式碼:

#採用非遞迴方法,使用二維陣列儲存中間過程
#三個出口條件,採用遞迴方法
import numpy as np
aar=[3,34,4,12,5,2]
def dp_subset(arr,S):
    subset=np.zeros((len(arr),S+1),dtype=bool)
    subset[:,0]=True
    subset[0,:]=False
    subset[0,arr[0]]=True
    for i in range(1,len(arr)):
        for s in range(1,S+1):
            if arr[i]>s:
                subset[i,s]=subset[i-1,s]
            else:
                A=subset[i-1,s-arr[i]]
                B=subset[i-1,s]
                subset[i,s]=A or B
                
    r , c = subset.shape
    return subset[r-1,c-1]

print(dp_subset(arr,9))
print(dp_subset(arr,10))
print(dp_subset(arr,11))
print(dp_subset(arr,12))

output:
True
True
True
False