【演算法學習】切割木棍問題——動態規劃
阿新 • • 發佈:2018-12-29
問題描述:
假設,有一條長度為n的木棍,已知木棍的銷售價格Pi與木棍長度i有關,i = 1,2,3,...n.問,怎樣切割能獲得最大收益。
長度為0的木棍收益肯定是0了,即profit[0] = 0.
切割長度(seg) | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
銷售價格(pi) | 1 | 5 | 8 | 9 | 10 | 17 | 17 | 20 | 24 | 30 |
對於長度為n的木棍,他的遞推關係是:profit[n] = max(pi[i] + profit[length - seg[i]]), 其中i = 1,2,3,...n;
暴力解決方法:
int Cut_Common(int seg[], int pi[], int arr_len, int source_len) { if (source_len == 0) return 0; int tmp = -1; for (int i = 0; i < arr_len; ++i) { if (source_len - seg[i] >= 0) tmp = max(tmp, pi[i] + Cut_Common(seg, pi, arr_len, source_len - seg[i])); } return tmp; }
這樣的解法,會發生多次相同的自問題求解,故效率非常差。聯想每次呼叫產生一個樹節點,葉節點代表了結束遞迴,O(2^n)的時間複雜度
為了解決對相同自問題的多次求解,故可用一個數組儲存自問題的解,下次需要時就不用再從新算。
動態規劃——自底向上方法:
解決的主要思路是,先求解長度為1的最大收益,再到2,3.....一直到n,而每次求解的解都儲存起來,時間複雜度為O(nm),空間複雜度為O(n)。int _Cut_Dynamic_DownToTop(int seg[], int pi[], int arr_len, int length, int dump[]) { int tmp; dump[0] = 0; for (int i = 1; i <= length; ++i) { tmp = -1; for (int j = 0; j < arr_len; ++j) { if (i - seg[j] >= 0) tmp = max(tmp, pi[j] + dump[i - seg[j]]); } dump[i] = tmp; } return dump[length]; } int Cut_Dynamic_DownToTop(int seg[], int pi[], int arr_len, int length) { int *dump = (int *)malloc(sizeof(int)*length + 1); int tmp = _Cut_Dynamic_DownToTop(seg, pi, arr_len, length, dump); free(dump); return tmp; }
自底向上的解法仍有瑕疵,那就是在求解給定問題時,有些較小問題的解常常是不必須的,而自頂向下 + 記憶功能 解決了這個問題。
動態規劃——自頂向下(+記憶功能):
int _Cut_Dynamic_TopToDown(int seg[], int pi[], int arr_len, int length, int dump[]) { if (dump[length] >= 0) return dump[length]; int tmp = -1; for (int i = 0; i < arr_len; ++i) { if (length - seg[i] >= 0) tmp = max(tmp, pi[i] + _Cut_Dynamic_TopToDown(seg, pi, arr_len, length-seg[i], dump)); } dump[length] = tmp; return dump[length]; } int Cut_Dynamic_TopToDown(int seg[], int pi[], int arr_len, int length) { int *dump = (int *)malloc(sizeof(int)*length+1); for (int i = 0; i <= length; ++i) { dump[i] = -1; } dump[0] = 0; int tmp = _Cut_Dynamic_TopToDown(seg, pi, arr_len, length, dump); free(dump); return tmp; }
注意到,自頂向下 和 暴力解決的 細微差別,自頂向下記錄了每一步的結果(這就是記憶功能),而暴力解決方法總是重新算結果。