正文之前
其實動態規劃老早之前就看過, 但是可惜的是印象不深,到今天徹底忘得差不多了,這兩天看《算法導論》終于讓我啃下了二叉搜索樹和紅黑樹兩個家伙,雖然還未曾熟練于胸,但是基本能用了。。?,F在看到了動態規劃,這就不得不來搞一搞了。。以一個最簡單的鋼條切割的示例來詳解下我的收獲
正文
首先發代碼,這個是自底向上的版本,其他的版本不做多的解釋,這個最好用最直觀。
#include <iostream>
#include<time.h>
using namespace std;
int max(int a,int b){
return a>b?a:b;
}
int BOTTOM_UP_CUT(int p[], int n){
int r[n];
r[0]=0;
for (int i = 1; i <= n; ++i)
{
int q=0;
for (int j = 1; j <= i && j<11; ++j)
{
q = max(q,p[j] + r[i-j]);
}
r[i] = q;
}
return r[n];
}
int main(){
//輸入數據
int p[11] = {0,1,5,8,9,10,17,17,20,24,30};
//輸入數據
clock_t start = clock();
cout<<BOTTOM_UP_CUT(p,10)<<endl;
cout<<"Usage of Time: "<<clock()-start<<endl;;
return 0;
}
結果也是喜人:
大概的意思就是:
0 段 ---> 售價:0
1 段 ---> 售價:1
2 段 ---> 售價:5
3 段 ---> 售價:8
4 段 ---> 售價:9
5 段 ---> 售價:10
6 段 ---> 售價:17
7 段 ---> 售價:17
8 段 ---> 售價:20
9 段 ---> 售價:24
10 段 ---> 售價:30
需要我們規劃一個算法來解出對于任意的長度n,又怎樣的切割方式來獲取最大的利潤?
那么,我們以自底向上的思想來考慮的話,對于任何一個長度n的鋼條,最大利潤下的分割方案總是由左邊的一段長度i和另外一段組合(還要繼續細分)長度n-i組成。假設最佳方案下的i不變,那么我們只要考慮n-i的分割方案。于是我們就可以考慮n-i長度的鋼條如何切割。(至于如何確定i?當然是遍歷了~)然后這么一直考慮下去,最后總歸會到1這個長度的。這樣的話就無法繼續細分。所以我們完全有:
那么代碼就很好解釋了
- 初始化r[0] = 0,這個意思是長度為0的時候總收益為0;
int BOTTOM_UP_CUT(int p[], int n){
int r[n];
r[0]=0;
對于左邊的i的長度,我們理所當然的從1開始算起,這樣的話,就可以直接利用r[1] = max(p[1],p[1]+r[0] )算出來了。這樣,當我們需要算r[2]的時候,就可以看看,到底是p[1]+r[1] 大還是p[2]+r[0]大了。。然后就這么一路平推過去。。
for (int i = 1; i <= n; ++i)
{
q是在一次i分段過程中,設置的最大收益寄存器。
int q=0;
這個過程就是來考察既定的左邊段長i下如何獲得右邊n-i的最大收益的循環了。別看一開始就用上了r,但是考慮到我們的i從1開始,所以一開始只需要r[0]就可以計算出r[1],等到后面要別的了。就會發現前面已經把所有的子問題都鋪墊好了。
for (int j = 1; j <= i && j<11; ++j)
{
q = max(q,p[j] + r[i-j]);
}
不得不說這個q真的用的妙,初始為0的話,完全就可以視作為r[0]使用。而后每一次循環結束都會刷新它的值,也就是對于n-i這一段,如果再把它細分,每一次細分之下的結果中的最大值會放到q中去,自然而然,一輪循環下來,q就存儲了r[n-i]的最大值了。。
r[i] = q;
}
return r[n];
}
正文之后
不縮了不縮了。。撤撤撤。。準備吃飯去了晚上還接了個音控崗的班還要去沖飲水卡。。賊惱火。。。