【動態規劃(一)】動態規劃基礎
1.1 動態規劃簡介
動態規劃(dynamic programming)是運籌學的一個分支,是求解決策過程(decision process)最優化的數學方法。20世紀50年代初美國數學家R.E.Bellman等人在研究多階段決策過程(multistep decision process)的優化問題時,提出了著名的最優化原理(principle of optimality),把多階段過程轉化為一系列單階段問題,利用各階段之間的關係,逐個求解,創立了解決這類過程優化問題的新方法——動態規劃。1957年出版了他的名著《Dynamic Programming》,這是該領域的第一本著作。
動態規劃要注意兩點:1,狀態;2,狀態轉移方程。
我不太願意詳細講解下面三個問題的思考過程,一步步推導沒有太大的必要,畢竟程式設計這種事情看一百遍,還不如自己在紙上畫一畫,然後敲敲程式碼。
希望能通過下面這三個小問題,來熟悉,學習動態規劃。
1.2 硬幣問題
問題:如果我們有面值為1元、3元和5元的硬幣若干枚,如何用最少的硬幣湊夠11元?
解析:
(1)湊夠1元,1個1元硬幣
(2)湊夠2元,2個1元硬幣
(3)湊夠3元,3個1元硬幣或者1個3元硬幣,又1<3,所以選擇一個3元硬幣。以此類推至11元。
(4)其實是在之前的基礎確定的。如,
2元=1元(1個硬幣)+1元(1個硬幣);//需2個硬幣
3元=2元(2個硬幣)+1元(1個硬幣)。
(5)用陣列Coin儲存硬幣種類,陣列Dp儲存相應所需最小硬幣個數,如Dp[m]=n,代表湊夠m至少需要n個硬幣。為方便使用,不使用Dp[0]。
(6)構建方程:Dp[i]=Dp[i-Coin[j]]+1;//加1代表需要Coin[j]這個硬幣
原始碼(簡單測試可用):Talk is cheap,show me the code!
int main()
{
int coin[Count]={1,3,5};//硬幣型別
int Dp[Sum+1];//記錄從0-11的銀幣數目
//演算法
int i,j;
//初始化,使每一個錢數為相應個數為1的硬幣構成
for(i=0;i<=Sum;i++)
{
Dp[i]=i;
}//Dp[0]=0不使用
for(i=1;i<=Sum;i++)
{
for(j=0;j<Count ;j++)
{
if(i>=coin[j]&&Dp[i-coin[j]]+1<Dp[i])
{
Dp[i]=Dp[i-coin[j]]+1;
}
}
}
//列印
cout<<Dp[Sum]<<endl;
for(i=1;i<=Sum;i++)
{
cout<<Dp[i]<<" ";
}
}
1.3 數塔問題
問題:從頂部出發在每一個節點可以選擇向左或者向右走,一直走到底層,
要求找出一條路徑,使得路徑上的數字之和最大.
解析:
(1)自底向上分析。先使用M二維陣列儲存該數塔,然後建立Dp二維陣列,儲存每個位置的和,如下圖
(2)每次從本層結點的2個分支結點中選出最大值,
構建方程:Dp[i][j]=Max{Dp[i+1][j],Dp[i+1][j+1]}+Arry[i][j];
原始碼(簡單測試可用):Talk is cheap,show me the code!
//數塔問題處理函式,自底向上尋找
void DataTower()
{
int i,j;
//初始化
for (i=0;i<Max;i++)
{
Dp[Max-1][i]=M[Max-1][i];//複製圖最後一列
}
//計算
for(i=Max-2;i>=0;i--)//最後一行已經賦值,還有Max-1行沒有賦值
{
for(j=0;j<=i;j++)//第i行就有i列
{
//找出左右子節點最大的一個
if(Dp[i+1][j]>Dp[i+1][j+1])
Dp[i][j]=Dp[i+1][j]+M[i][j];
else
Dp[i][j]=Dp[i+1][j+1]+M[i][j];
}
}
}
列印,來源網上
//由Dp陣列,列印最終結果
void print ()
{
cout << "最大路徑和:" << Dp[0][0] << '\n';
int node_value;
// 首先輸出塔頂元素
cout << "最大路徑:" << M[0][0];
int j = 0;
for (int i = 1; i < Max; ++i)
{
node_value = Dp[i - 1][j] - M[i - 1][j];
if (node_value == Dp[i][j + 1]) ++j;
cout << "->" << M[i][j];
}
cout << endl;
}
1.4 最長非降子程式
問題:5,3,4,8,6,7
求該數列的最長非降子序
解析:
(1)假設該序列只有一個”5”,那麼最長為1;
(2)“5,3”,轉換來看“5(1),3(1)”,最長為1;
(3)“5,3,4”,轉換“5(1),3(1),4(2)”,因為最長非降序列為“3,4”,最長為2;
(4)不一一列舉,看看這條更清楚一些,“5(1),3(1),4(2),8(3),6(3)”。“6”的確定是因為“4”,“3,4,6”在“4”對應的長度上加1,為3;
(5)建立陣列Dp,構建方程:Dp[i]=Dp[j]+1;
判斷條件是A[i]>=A[j]。
原始碼(簡單測試可用):Talk is cheap,show me the code!
本人依據以上思路寫的。
void Long01(int *A)
{
int Dp[Max];//記錄i位置對應的相應的非降子序的個數
int i,j;
Dp[0]=1;//第一個元素為肯定為1
for (i=1;i<Max;i++)
{
if(A[i]>=A[i-1])
{
Dp[i]=Dp[i-1]+1;
}
else
{
for(j=i-1;j>=0;j--)
{
if(A[i]>A[j])
{
Dp[i]=Dp[j]+1;
break;
}
}
if(j<0)
{
Dp[i]=1;
}
}
}
}
網上跟上述思路一致,但是更簡潔
void Long02(int *A)
{
int d[Max];
int len = 1;
for(int i=0; i<Max; ++i){
d[i] = 1;
for(int j=0; j<i; ++j) //迴圈i前面的幾個元素
if(A[j]<=A[i] && d[j]+1>d[i]) //i前可能存在多個小於i的值,取最大者,個人覺得此處倒著找好一些
d[i] = d[j] + 1;
if(d[i]>len) len = d[i];
}
cout<< len<<endl;
}
1.5 總結
比較簡單,如果有錯誤的地方,煩請大牛指教,上述的程式碼是在Vs2010中編寫的,如果有亂碼問題,可以在:https://github.com/AngryCaveman/C-Struct.git中檢視下載“018番外”資料夾下本人的原始碼,其他資料夾中專案是在Vs2013中編寫的。
相關文章
- 動態規劃動態規劃
- 動態規劃 區間dp 基礎題動態規劃
- [leetcode] 動態規劃(Ⅰ)LeetCode動態規劃
- 動態規劃法動態規劃
- 模板 - 動態規劃動態規劃
- 動態規劃初步動態規劃
- 動態規劃分析動態規劃
- 動態規劃(DP)動態規劃
- c++踩方格-動態規劃基礎題C++動態規劃
- 禮物的最大價值(一維動態規劃&二維動態規劃)動態規劃
- 演算法系列-動態規劃(1):初識動態規劃演算法動態規劃
- 動態規劃小結動態規劃
- [leetcode 1235] [動態規劃]LeetCode動態規劃
- 動態規劃專題動態規劃
- 動態規劃-----線性動態規劃
- 好題——動態規劃動態規劃
- 動態規劃初級動態規劃
- 淺談動態規劃動態規劃
- 3.動態規劃動態規劃
- 動態規劃題單動態規劃
- 動態規劃 總結動態規劃
- 雙序列動態規劃動態規劃
- 動態規劃方法論動態規劃
- [atcoder 358] 【動態規劃】動態規劃
- 區間動態規劃動態規劃
- 動態規劃(Dynamic programming)動態規劃
- 有關動態規劃動態規劃
- 一維動態規劃總結動態規劃
- 一文搞懂動態規劃動態規劃
- 演算法基礎--遞迴和動態規劃演算法遞迴動態規劃
- 動態規劃之數的劃分動態規劃
- leetcode題解(動態規劃)LeetCode動態規劃
- [動態規劃] 區間 dp動態規劃
- (C++)DP動態規劃C++動態規劃
- 【CodeChef】Graph Cost(動態規劃)動態規劃
- leetcode總結——動態規劃LeetCode動態規劃
- 動態規劃練習題動態規劃
- 大盜阿福(動態規劃)動態規劃
- 動態規劃做題思路動態規劃