動態規劃這個演算法,我一直都搞不明白,也許因為我數學能力太差的緣故,總是不得其要領,每次學習這個演算法的時候,總是不知道所謂的狀態轉移方程到底是怎麼樣推導出來的。其實就在我寫這篇部落格的時候,我依然不清楚。什麼問題能用動態規劃來解決呢?動態規劃問題的特徵就是最優子結構,乙個遞迴結構:
該問題需要求乙個最優解
該問題重複包含著子問題
比如說經典的動態規劃問題,最長公共子串行(如下所述),我們要求乙個最長的子串行,首先這是乙個最優解,其次,兩個序列的最長公共序列,也同時包含著子串行的最長公共序列。
動態規劃通常按以下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直接搜尋,對每個狀...