動態規劃(dp)通過分解成子問題解決了給定複雜的問題,並儲存子問題的結果,以避免再次計算相同的結果。我們通過下面這個問題來說明這兩個重要屬性:
1)重疊子問題
2)最優子結構
1)重疊子問題:
像分而治之,動態規劃也把問題分解為子問題。動態規劃主要用於:當相同的子問題的解決方案被重複利用。在動態規劃中,子問題解決方案被儲存在乙個表中,以便這些不必重新計算。因此,如果這個問題是沒有共同的(重疊)子問題, 動態規劃是沒有用的。例如,二分查詢不具有共同的子問題。下面是乙個斐波那契函式的遞迴函式,有些子問題被呼叫了很多次。
1
/* ****** recursive program for fibonacci numbers */
2
int
fib(
int
n)
3
執行 fib(5) 的遞迴樹
1
fib(5)
2
/ \
3
fib(4) fib(3)
4
/ \ / \
5
fib(3) fib(2) fib(2) fib(1)
6
/ \ / \ / \
7
fib(2) fib(1) fib(1) fib(0) fib(1) fib(0)
8
/ \
9
fib(1) fib(0)
我們可以看到,函式f(3)被稱執行2次。如果我們將儲存f(3)的值,然後避免再次計算的話,我們會重新使用舊的儲存值。有以下兩種不同的方式來儲存這些值,以便這些值可以被重複使用。
a)記憶化(自上而下):
b)打表(自下而上):
一)記憶化(自上而下):記憶化儲存其實是對遞迴程式小的修改,作為真正的dp程式的過渡。我們初始化乙個陣列中查詢所有初始值為零。每當我們需要解決乙個子問題,我們先來看看這個陣列(查詢表)是否有答案。如果預先計算的值是有那麼我們就返回該值,否則,我們計算該值並把結果在陣列(查詢表),以便它可以在以後重複使用。
下面是記憶化儲存程式:
01
/* memoized version for nth fibonacci number */
02
#include
03
#define nil -1
04
#define max 100
05
06
int
lookup[max];
07
08
/* function to initialize nil values in lookup table */
09
void
_initialize()
10
15
16
/* function for nth fibonacci number */
17
int
fib(
int
n)
18
26
27
return
lookup[n];
28
}
29
30
int
main ()
31
一)打表(自下而上)
下面我們給出自下而上的打表方式,並返回表中的最後一項。
view source
01
/* tabulated version */
02
#include
03
int
fib(
int
n)
04
13
14
int
main ()
15
這兩種方法都能儲存子問題解決方案。在第乙個版本中,記憶化儲存只在查詢表儲存需要的答案。而第二個版本,所有子問題都會被儲存到查詢表中,不管是否是必須的。比如lcs問題的記憶化儲存版本,並不會儲存不必要的子問題答案。
1277 統計全為 1 的正方形子矩陣(動態規劃)
1.問題描述 2.思路分析 一開始的時候對於也想到了應該會是動態規劃的思路來解決,但是沒有想到具體的解決方案,動態規劃的難點一般也是在這裡,在官方的題解中提供的思路是使用乙個二維的list列表來記錄中間的結果,dp i j 表示以當前的i,j位置的正方形的個數,能夠想到這個思路也是非常巧妙的,當是第...
動態規劃解不包含相同數字的子串個數問題
比賽描述 仙靈女巫露露,對於魔法的熱忱可是超出常人,要是發現了什麼上古遺留下的魔法,她總是想方設法地獲得,然後研究分析。而最近,他又從 小法師維嘉那裡獲得了乙個 奇怪 的魔法卷軸 這個魔法卷軸上有一大串數字,而且根據卷軸上的描述,這個魔法的威力指數來自於這一串數字中 魔法區間 的數量 所謂 魔法區間...
1003 求最大連續子串行和的動態規劃問題
解題思路 在最初看到題目的時候,因為動態規劃往往要存很多中間變數,因此想到陣列是最方便的。但是要注意的是,題目中的n數值非常大,如果都採用陣列來儲存的話,會出現記憶體不足的情況,因此可以考慮用其他的方法。在本題中,可以分析到,在每次讀入乙個新的資料時,都可以根據新讀入的資料和過去的一些記錄的比較,來...