演算法導論 動態規劃

2021-08-01 11:30:50 字數 3731 閱讀 1717

動態規劃這個演算法,我一直都搞不明白,也許因為我數學能力太差的緣故,總是不得其要領,每次學習這個演算法的時候,總是不知道所謂的狀態轉移方程到底是怎麼樣推導出來的。其實就在我寫這篇部落格的時候,我依然不清楚。

什麼問題能用動態規劃來解決呢?動態規劃問題的特徵就是最優子結構,乙個遞迴結構:

該問題需要求乙個最優解

該問題重複包含著子問題

比如說經典的動態規劃問題,最長公共子串行(如下所述),我們要求乙個最長的子串行,首先這是乙個最優解,其次,兩個序列的最長公共序列,也同時包含著子串行的最長公共序列。

動態規劃通常按以下4個步驟來設計乙個演算法:

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

遞迴地定義最優解的值

計算最優解的值(通常採用自底向上的方法)

得用計算出的資訊構造乙個最優解

動態規劃往往還結合一些備忘方法,用於記錄中間解,以免重複求解子問題。下面來看幾個問題:

給定乙個鋼條切割的長度和**,求出最佳的切割方案。且看下列:

長度(i)12

3456

78910

**(pi)15

891017

1720

2430

假設我們切割一條長度為 4 的鋼條,總共有以下幾種方案:

1. 不切割,**為 9

2. 1 和 3 :**為 1 + 8 = 9

3. 2 和 2 :**為 5 + 5 = 10

4. 3 和 1 :**為 8 + 1 = 9

5. 1、2、1 :**為 1 + 5 + 1 = 7

6. 1、1、2 :**為 1 + 1 + 5 = 7

7. 2、1、1 :**為 2 + 1 + 1 = 7

可知道分割一條長度為4的鋼條,最優的切割方案為「方案2」。更通俗一點,我們可以令rn為切割長度為n的鋼條,可以很輕鬆的得到以下公式:

這樣,**就容易寫了,如下所示:

#include 

#include

#include

int rodcut_recursive_down_to_up(int* p, int* s, int* k, int m, int n)}}

return q;

}int rodcut_recursive_up_to_down(int* p, int* s, int* k, int m, int n)

}return q;

}int main(void)

}free(p);

free(k);

free(s);

}return

0;}

輸入**表,然後輸入切割的長度,輸出最優方案,結果如下所示:

以上提供了兩種方案,一種是自頂向下的遞迴,一種是自底向上的遞迴設計,從**中我們可以看到兩者的區別,自頂向下,是先求解最後的結果,然後遞迴求解至第乙個結果,而自底向上則是先求解第乙個結果,直至最後乙個結果。

最長公共子串行是這樣乙個問題:有兩個字串s和t,求出兩者最長的公共子串行,如以下字串:

s:abcbdab

t:bdcaba

其中的最長公共子串行是 bdab、bcba、bcab等。

令c[i, j]表示從i到j的最長公共子串行,我們可以推導出以下狀態轉移式:

如果i到達字串s的長度或j到達字串t的長度,則最長公共子串行的長度為0

如果s[i] == t[j],那麼最長公共子串行即為後續子串行的最長公共子串行+當前字元長度(1)

如果s[i] != t[j],那麼最長公共子串行即為取s[i]的後續最長公共子串行與取t[i]的後續最長公共子串行的較大者(最後結果中未必有s[i]或t[j])

根據上述說明,寫出的**如下所示:

#include 

#include

#include

int longestcommonsubsequence(char* s, char* t, char** u, int ss, int ts)

else

return0;}

int main (void)

scanf("%s", s);

scanf("%s", t);

printf("%d\n\n", longestcommonsubsequence(s, t, u, 0, 0));

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

int l = 0, r = 0;

while(u[l][r])

else

if(u[l][r] == u[l][r + 1])

++r;

else

++l;

}printf("\n");

free(s);

free(t);

for(int i = 0; i != m + 1; ++i)

free(u[i]);

free(u);

}return

0;}

輸入字串s和t,ss表示s串的起點,ts表示t串的起點,u記錄當前位置的最長公共子串行長度。輸出的結果如下所示:

從以上圖表可以看到,我們要拿到最長的公共子串行,則需要從【0, 0】點開始遍歷表,我們只能往右或往下遍歷,如果【i, j】點的兩個字元s[i]和t[j]相等,那麼我們應該走對角線,如果【i, j】== 【i, j + 1】那麼我們應該往右走,否則應該往下走。如以上**所示。

給定乙個整數序列,求其中的乙個連續子串行,使其和最大。如給定以下序列:

-2 11 -4 13 -5 -2, 其最大連續子串行和為: 11 -4 + 13 = 20

定義sum[i]為最大子串行和,可以推導出以下狀態轉移方程:

其實,這個方程,我並不知道是怎麼樣推導出來的,前面乙個好理解,不過為什麼要和當前值做比較取較大者,我真的不明白。希望有人能給我指點迷津。根據以上方程,我寫出的**如下所示:

#include 

#include

#include

int maxsum = 0;

int maxsequencesum(int* num, int s, int e)

return sum;

}return0;}

int main(void)

return

0;}

程式的輸出結果,如下圖所示:

還有一種更簡單的寫法:

int maxsumsubsequence(const

int a, int n)

return maxsum;

}

這段**是網上抄的,我一開始真沒想到這麼寫。我的水平還有很大的提公升空間。

以上**本人親自編寫,經本人測試基本無誤,也許編寫方式和網上流傳有所不同,如有錯漏,請批評指正。

動態規劃這個演算法真的實用性很大,可惜我依然是半知半解,之所以厚臉皮寫出這篇文章,也是希望有一日能被某個高人碰巧看到這篇拙劣的博文,指點一下我這個苦苦自學,身邊沒朋友諮詢的小弟,在此感謝先。

演算法導論 2 動態規劃

動態規劃與分治方法類似,都是通過組合子問題的解來求解原問題 需要注意的是,動態規劃 dynamic programming 這裡的programming並不是指編寫電腦程式,而是指一種 法 分治方法將問題劃分為互不相交的子問題,遞迴地求解子問題,再將它們的解組合起來,求出原問題的解 動態規劃也是通過...

動態規劃 鋼條切割《演算法導論》

給定各長度鋼鐵單位 以及乙個長度為n的鋼條,求最大效益 1 10 分別為p include includeint memorized cut rod aux int p,int len,int r 樸素遞迴演算法 演算法複雜度為2 n級這裡寫 int cut rod int p,int len in...

演算法導論 動態規劃 鋼條切割

這幾天一直在看動態規劃的問題,看了看也沒想象中那麼難,但需要好好的分析一下問題,我們就拿演算法導論的鋼條切割問題好好理解一下,鋼條切割問題就是給你一段鋼條,讓你把鋼條切割成好多份,因為每一段鋼條都有不同的價值,我們要找到最優的切割方案,使切割成的鋼條價值最大。這道題我們可以用dfs直接搜尋,對每個狀...