動態規劃入門step-by-step

問題陳述

下面是一道簡單的動態規劃題目,主要是經過這道題介紹動態規劃的通常思考流程。大神請無視。app

有連續的n個房子,每一個房子有必定的金幣。當小偷偷取兩個連續的房子時會引起報警,請問這個小偷最多能偷多少金幣?dom

假設有A[1...n]一共n個房子,房子i有A[i]個金幣spa

方法1


如上圖所示。咱們首先考慮第一個房子A[1],若是咱們的最優解包含第一個房子A[1]則不包含A[2],那麼最優解就爲A[1]+A[3...n];若是不包含A[1]則最優解就在A[2...n]區間內。
即:
最優解:
\[ S(1,n) = max(A[1] + S(3,n), S(2,n)) \]
根據這個公式寫成代碼有code

def Steal(input, start, end):
    if start == end:
        return input[start]
    elif end - start == 1:
        return max(input[start],input[end])
    else:        
        return max(input[start]+Steal(input,start+2, end), Steal(input, start+1,end))

gold = []
for i in xrange(30):
    gold.append(random.randint(0,10))

start = time.clock()
print Steal(gold, 0, 29)
print 'duration= ',time.clock() - start

能夠看到結果爲:
blog

方法2


能夠看到方法1在遞歸調用過程當中有相同的節點,所以能夠採用打表的方法對沿途的節點值進行計算以免二次計算。遞歸

def Steal_mark_table(input, start, end, table):
    if start == end:
        return input[start]
    elif end - start == 1:
        return max(input[start],input[end])
    else:
        temp1 = 0
        temp2 = 0
        if table[start+2]!=0:
            temp1 = table[start+2]
        else:
            temp1 = Steal_mark_table(input,start+2, end, table)
            table[start+2] = temp1
        if table[start+1]!=0:
            temp2 = table[start+1]
        else:
            temp2 = Steal_mark_table(input, start+1,end, table)
            table[start+1] = temp2
        return max(input[start]+temp1, temp2)

gold = []
for i in xrange(30):
    gold.append(random.randint(0,10))

start = time.clock()
mark_table = [0]*30
print Steal_mark_table(gold, 0, 29, mark_table)
print 'duration= ',time.clock() - start
print mark_table

結果爲:

能夠看到速度獲得了極大的提高。input

方法3

通常可以使用自頂向下遞歸表示的動態規劃解法都會有自低向上的非遞歸表示方法。
咱們從前向後看io

\[ \begin{align} S(1)& = A[1]\\\ S(1,2)& = max(A[1],A[2])\\\ S(1,3)& = max(A[1]+A[3], A[2])\\\ S(1,n)& = max(A[n]+S(1,n-2), S(1,n-1))\\\ \end{align} \]table

對應的代碼爲class

def Steal_non_recur(input, start, end):
    table = [0]*len(input)
    table[0] = input[0]
    table[1] = max(input[0], input[1])
    for i in xrange(start+2, end+1):
        table[i] = max(input[i] + table[i-2], table[i-1])
    return table[end]

start = time.clock()
print Steal_non_recur(gold,0,29)
print 'duration= ',time.clock() - start

運行的結果爲:

能夠看到速度又有了不小的提高。

拓展

若是房子是環形排列的呢?即A[n]以後就是A[1]。
這樣咱們要分三種狀況考慮:

  • 既不包含A[n],也不包含A[1],即S(2,n-1)

  • 包含A[1]但不包含A[n],即A[1]+S(3,n-1)

  • 不包含A[1]但包含A[n],即A[n]+S(2,n-2)

最終結果取這三者最大值便可。

相關文章
相關標籤/搜索