動態規劃筆記
由於自己是非計算機專業,而對編程學習比較感興趣,特學習算法導論一書,根據書上僞代碼來練習,一來提高自己算法思維,二來督促自己好好學習,不能虛度光陰。
一:思想
動態規劃,通常用來求解最優化問題,這類問題有很多可行解,我們希望尋找其最優解,通常是最大值最小值。
具體步驟就是一個大問題可以分爲很多子問題,通過求解小規模的子問題,進而組合子問題得到原問題的解。使用動態規劃解決問題,需要具備以下兩個因素。
- 最優子結構:一個問題的最優解包含子問題的最優解。也就是一個最優解,它也必須是字問題的最優解。
- 重疊子問題:問題的遞歸算法要重複求解相同的子問題,而不是一直生成新問題。
二:例題
1 鋼條切割問題
對於長度爲 i 的鋼條的價格
- 最優子結構:當鋼條完成首次切割後,變成兩段獨立的鋼條。進而求解兩段獨立的鋼條最優解,然後相加即可。
- 重疊子問題:鋼條越切越少,則子問題重疊的越多。
我們將鋼條從左邊切下的長度記爲i的那一段,只對右邊長度n-i的一段繼續進行切割(遞歸求解),對左邊的不再進行切割。即問題轉化爲:第一段長度求解如下最大值:.rn=max(pi+pn−i),i=1...n
自頂向下法自然求解
所謂自頂向下法,是直接利用遞歸的算法求解原問題。再次期間遞歸調用會繼而求解小規模問題。
此算法可以改進,因此它會多次重複的計算規模更小的子問題。解法方法是引入備忘錄機制,當
//直接遞歸計算
//int cut_rod(int p[],int n)
//{
// if(n==0)
// return 0;
// int q=-1;
// for(int i=1;i<=n;i++)
// {
// int temp=p[i]+cut_rod(p,n-i);
// if(q<temp)
// {
// q=temp;
// }
// }
// return q;
//}
//帶備忘錄:先定義一個數組存放計算過的收益,此數組不能放到遞歸裏。
//int Memoized_cut_Rod(int p[],int n)
//{
// vector<int>r(n,-1);
// return Memoized_cut_Rod_Aux(p,n,r);
//}
//int Memoized_cut_Rod_Aux(int p[],int n,vector<int>r)
//{
// int q=-1;
// if(r[n]>=0)
// return r[n];
// if(n==0)
// q=0;
// else
// {
// for(int i=1;i<=n;i++)
// {
// int temp=p[i]+Memoized_cut_Rod_Aux(p,n-i,r);
// if(q<temp)
// {
// q=temp;
// }
// }
// r[n]=q;
// }
// return q;
//
//}
自底向上法求解小規模問題
自底向上法是直接先求解小規模問題,在小規模問題上逐步求解大規模問題。
//int Bottom_up_cut_rod(int p[],int n)
//{
// vector<int>r;//保存切割的最優值
// vector<int>s;//保存切割點
// r.push_back(0);
// s.push_back(0);
// for(int j=1;j<=n;j++)//逐步求解規模j的問題,一直求解到n,即原問題
// {
// int q=-1;
// for(int i=1;i<=j;i++)
// {
// int temp=p[i]+r[j-i];
// if(q<temp)
// {
// q=temp;
// s.push_back(i);
// }
// }
// r.push_back(q);
// }
// return r[n];
//}
//
實例計算
int main()
//{
// int p[]={0,1,5,8,9,10,17,17,20,24,30};
// cout<<cut_rod(p,5)<<endl;
// cout<<Memoized_cut_Rod(p,5)<<endl;
// cout<<Bottom_up_cut_rod(p,5)<<endl;
// getchar();
//
//}
2 矩陣鏈相乘問題
給定n個矩陣序列