動態規劃之鋼條切割問題,《演算法導論》15.1
動態規劃(dynamic programming),是一種解決問題的方法,它的思路是將大問題分解為子問題,然後合併子問題的解。與分治法將問題分解為彼此獨立的子問題不同,動態規劃應用於子問題相互重疊的情況。為了避免這些重疊部分(即子子問題)被重複計算,動態規劃演算法對每個子子問題只計算一次,並記錄在一個表格中,這正是programming名字的由來,取其表格之意。
動態規劃通常用來求解“最優化問題”,這類問題可以有多種解決方案,每種解決方案對應一個值,我們希望找到具有最大值或最小值(最優值)的解決方案。注意,最優值只有一個,但是最優解決方案可能有多個,因為可能同時有多個解決方案都達到最優值。
We typically apply dynamic programming to optimization problems. Such problems can have many possible solutions. Each solution has a value, and we wish to find a solution with the optimal (minimum or maximum) value. We call such a solution an optimal solution to the problem, as opposed to the optimal solution,since there may be several solutions that achieve the optimal value.
解決這類問題的關鍵是找出所有的子問題,以及子問題之間的依賴關係。
When we think about a dynamic-programming problem,we should understand the set of subproblems involved and how subproblems depend on one another.
鋼條切割問題,給定一段長度為n的鋼條和一個價格表pi(i = 1,2,...,n),求切割鋼條方案,使得銷售收益r(n)最大。
#include<iostream> using namespace std; int p[10] = {1,5,8,9,10,17,17,20,24,30}; //p[i] means price when length = i+1 //引數p代表“長度-價格對照表”,n代表待切割鋼條總長度。函式列印最優值和一個最優解決方案,並返回最優值。 //這是一個從r[1]~r[n]自底向上的求解過程,耗時O(n^2) int bottom_up_cut_rod(int p[], int n) { int* r = new int[n+1]; //r[i] means max revenue when length = i int* s = new int[n];//s[i] means first piece size when length = i+1 r[0] = 0; int rx,sx,tmp; for(int i = 1; i != n+1; ++i) { rx = -1; for(int j = 0; j != i; ++j) { tmp = p[i-j-1] + r[j]; if(rx < tmp) { rx = tmp; sx = i-j; } } r[i] = rx; s[i-1] = sx; } //print the optimal value and a optimal solution cout << "r" << n << " = " << r[n] << ", 切割方案 " << n << " = "; for(int l = n; l != 0; ) { cout << s[l-1]; l = l - s[l-1]; if(l) cout << "+"; } cout << endl; rx = r[n]; delete[] r; delete[] s; return rx; } void test() { for(int n = 0; n != 11; ++n) bottom_up_cut_rod(p, n); } /*output: r0 = 0, 切割方案 0 = r1 = 1, 切割方案 1 = 1 r2 = 5, 切割方案 2 = 2 r3 = 8, 切割方案 3 = 3 r4 = 10, 切割方案 4 = 2+2 r5 = 13, 切割方案 5 = 3+2 r6 = 17, 切割方案 6 = 6 r7 = 18, 切割方案 7 = 6+1 r8 = 22, 切割方案 8 = 6+2 r9 = 25, 切割方案 9 = 6+3 r10 = 30, 切割方案 10 = 10 */
- 課後習題
15.1-2 用總長度為3的鋼條就可以舉出反例。
假設我們要對長度為3的鋼條進行切割,根據鋼條密度的定義,當鋼條長度為1、 2、 3時,密度依次為d1= p1, d2 = p2 / 2, d3 = p3/3;
下面讓我們列舉所有可能切割方案以及總收益r:
方案1:3 = 3, r1 = d3 * 3;
方案2:3 = 2+1, r2 = d2 * 2 + d1;
方案3:3 = 1+1+1, r3 = d1 * 3;按照題目所描述的“貪心”策略,在已知d2是三個密度中最大的情況下,就一定能得出方案2是最優解。
用公式表達,就是:已知d(2)>d(1),d(2) > d(3), 有r2>r1,r2>r3。成不成立呢?已知d2>d1, 可得r2-r3 = 2(d2-d1) > 0, 於是r2>r3得證。
而r2-r1 = 2(d2-d3)+(d1-d3), 只要(d1-d3)足夠小,就可以令r2-r1 < 0,就構成了反例。
比如d1=1, d2=5, d3=4 就構成一個反例。