動態規劃學習(一)

2021-07-29 05:16:16 字數 3573 閱讀 4730

分治方法將問題劃分為互不相交的子問題,遞迴地求解子問題,再將它們的解組合起來,求出原問題的解。與之相反,動態規劃應用於子問題重疊的情況,即不同的子問題具有公共的子子問題。對於分治方法,在處理問題的時候,會反覆求解那些公共子子問題。而動態規劃演算法對每個子子問題只求解一次,將其儲存在乙個**中,從而無需每次求解乙個子子問題,避免了這種不必要的計算工作。

通俗來講:就是你去考研,需要考政治,英語,數學和專業課這四門,只有把這四門成績達到一定分數才行。這樣考研這個問題就分解為提高每一門科目的成績,只有把這些子問題解決了,最終你才能成功讀研。因為要對學好每一門課程,所以相對來講它的開銷很大。

動態規劃方法通常用來求解最優化問題。這類最優化問題一般來說都有很多的解,每個解都對乙個值,我們希望尋找具有最優值(最大值或者最小值)的解。我們稱這樣的解為問題的乙個最優解,因為可能存在多個解都達到最優值。

我們通常按這4個步驟來設計乙個動態規劃演算法:

1. 刻畫乙個最優解的結構特徵

2. 遞迴地定義最優解的值

3. 計算最優化的值,通常採用自底向上的方法

4. 利用計算的資訊構造乙個最優解

運用動態規劃方法求解的最優化問題應該具備的兩個要素:最優子結構和子問題重疊

用動態規劃方法求解最優化問題的第一步就是刻畫最優解的結構。如果乙個問題的最優解包含其子問題的最優解,我們就稱此問題具有最優子結構性質。

例如:無權最短路徑問題:找到一條從u到v的邊數最少的簡單路徑。

我們可以將路徑u->v的路徑看為p,顯然,我們可以分解該路徑變為u->w->v,其中u->w的路徑為p1,w->v的路徑為p2,要想使p最短,p1和p2也應該是最短的,所以此問題具有最優子結構。

適用動態規劃方法求解的最優化問題應該具備的第二個性質就是子問題空間必須足夠「小」,即問題的遞迴演算法會反覆地求解相同的子問題,而不是一直生成新的子問題。如果遞迴演算法反覆求解相同的子問題,我們就成最優化問題具有重疊子問題性質。與之相對的,適用於分治方法求解的問題通常在遞迴的每一步都生成全新的子問題。動態規劃演算法通常這樣利用重疊子問題性質:對每個子問題求解一次,將解存入乙個表中,當再次需要這個子問題時直接查表,每次查表的代價為常量時間。

如果問題不具備子問題重疊,即每次產生的都是新的問題,那麼動態規劃演算法中將值存入表中就毫無意義了。

給定一段長度為n英吋的鋼條和乙個**表p(i)(i = 1, 2, … , n),求切割鋼條方案,是的銷售收益r(n)最大

**如下:

int cut_rod(const

vector

& price_list, int steel_length)

int max_profit = int_min;

for (int i = 1; i <= steel_length; ++i)

return max_profit;

}

在實際引用中,效率相當差,它所做的工作量隨鋼條的長度增大呈指數上公升,原因在於它反覆求解子問題。

對於通過遞迴求解,由於它反覆求解相同的子問題。因此,動態規劃方法仔細安排求解順序,對每個子問題只求解一次,並將結果儲存下來。如果隨後再次需要此子問題的解,只需要查詢儲存的結果,而不必重新計算。因此,動態規劃方法是付出額外的記憶體空間來節省計算時間,是典型的時空權衡的例子

帶備忘錄的自頂向下法

通過將子問題結果儲存在乙個陣列中從而實現。

// 方法一:帶備忘錄的自頂向下法

int memorized_cut_rod_aux(const

vector

&, int, vector

&);int memorized_cut_rod(const

vector

& price_list, int steel_length)

int memorized_cut_rod_aux(const

vector

& price_list,

int steel_length,

vector

& memorandum)

int max_profit = int_min;

if (steel_length == 0)

else

}memorandum[steel_length] = max_profit;

return max_profit;

}

自底向上法

相比較來說,自頂向上法由於沒有運用遞迴,所以它的時間複雜度通常具有更小的係數。

int bottom_up_cut_rod(const

vector

& price_list, int steel_length)

memorandum[i] = max_profit;

}return memorandum[steel_length];

}

自底向上的動態規劃方法處理子問題中的頂點的順序為:對於乙個給定的子問題x,在求解它之前求解鄰接至它的子問題y。換句話說就是,對於任何子問題,直至它依賴的所有子問題均已求解完成,才會求解它。在本題目中,要解決長度為4的最優解,只要求解1的時候它的解是最優的,在求解2的時候它的解也是最優的,求解3的時候它的解是最優的,那麼求解4的時候他的解也是最優的。

鋼條切割問題

給定兩個序列x=[x1, x2, x3, … , xn]和 y=[y1, y2, y3, … , yn],求x和y長度最長的公共子串行

#include 

#include // for pair

#include

using

namespace

std;

// 動態規劃求解最長公共子串行問題

std::pair>, vector

>> lcs_length(const

string& x_str, const

string& y_str)

for (int i = 1; i <= x_str_size; ++i)

for (int j = 0; j <= y_str_size; ++j)

for (int i = 0; i < x_str_size; ++i)

else

if (vv_num[i][j + 1] >= vv_num[i + 1][j])

else }}

return

std::make_pair(vv_num, vv_ch);

}void print_lcs(const

vector

>& vv_ch, const

string& x_str, int i, int j)

if (vv_ch[i][j] == '1')

else

if (vv_ch[i][j] == '2')

else

}int main(int argc, char

const *argv)

cout << endl;

}*/print_lcs(result.second, x_str, x_str.size(), y_str.size());

return

0;}

最長公共子串行問題

《演算法導論原書第三版》第十五章動態規劃

動態規劃學習筆記 一

春秋招如同達摩克里斯之劍,逼著我又撿起來這些亂七八糟的演算法書。這裡分享一些關於動態規劃的一些學習筆記。動態規劃 dynamic programming 其實一開始是在優化理論出現的,他不是計算機裡的東西,但是他卻在計算機裡扮演了乙個非常重要的角色,是無論如何也繞不開的一種演算法。回想一下,我們學過...

動態規劃學習

首先是01揹包問題,可以把它看做是乙個 行和列分別是體積從1,2,v,每個物品的花費c1,c2,cn,每個空格裡的數代表放入第i個物品在體積為j的揹包裡的最大價值。dp j 表示體積為j的揹包,放入物品後,可以得到的最大值。狀態轉移方程為if j c i dp j max dp j dp j c i...

動態規劃學習

把位置 i,j 看成乙個狀態,然後定義狀態 i,j 的指標函式d i,j 從位置 i,j 出發時能得到的最大和。int i,j 邊界值處理 for j 1 j n j result n j data n j 由下向上動態規劃,儲存葉子節點到當前節點的最大值 for i n 1 i 1 i for j...