c++動(dòng)態(tài)規(guī)劃經(jīng)典算法
基本思想
動(dòng)態(tài)規(guī)劃算法通常用于求解具有某種最優(yōu)性質(zhì)的問(wèn)題。在這類(lèi)問(wèn)題中,可能會(huì)有許多可行解。每一個(gè)解都對(duì)應(yīng)于一個(gè)值,我們希望找到具有最優(yōu)值的解。動(dòng)態(tài)規(guī)劃算法與分治法類(lèi)似,其基本思想也是將待求解問(wèn)題分解成若干個(gè)子問(wèn)題,先求解子問(wèn)題,然后從這些子問(wèn)題的解得到原問(wèn)題的解。與分治法不同的是,適合于用動(dòng)態(tài)規(guī)劃求解的問(wèn)題,經(jīng)分解得到子問(wèn)題往往不是互相獨(dú)立的。若用分治法來(lái)解這類(lèi)問(wèn)題,則分解得到的子問(wèn)題數(shù)目太多,有些子問(wèn)題被重復(fù)計(jì)算了很多次。如果我們能夠保存已解決的子問(wèn)題的答案,而在需要時(shí)再找出已求得的答案,這樣就可以避免大量的重復(fù)計(jì)算,節(jié)省時(shí)間。我們可以用一個(gè)表來(lái)記錄所有已解的子問(wèn)題的答案。不管該子問(wèn)題以后是否被用到,只要它被計(jì)算過(guò),就將其結(jié)果填入表中。這就是動(dòng)態(tài)規(guī)劃法的基本思路。具體的動(dòng)態(tài)規(guī)劃算法多種多樣,但它們具有相同的填表格式。
重要分析問(wèn)題方法
動(dòng)態(tài)規(guī)劃算法跟數(shù)組有著密切的關(guān)系,因此推薦大家在分析動(dòng)態(tài)規(guī)劃的算法時(shí)畫(huà)一張表格(建議使用excel)分析解決問(wèn)題往往能夠事半功倍。
動(dòng)態(tài)規(guī)劃算法實(shí)例
1、臺(tái)階問(wèn)題
問(wèn)題描述:有10級(jí)臺(tái)階,一個(gè)人每次上一級(jí)或者兩級(jí),問(wèn)有多少種走完10級(jí)臺(tái)階的方法。
結(jié)合表格分析問(wèn)題,每個(gè)子問(wèn)題都只跟臺(tái)階這個(gè)變量相關(guān),可以畫(huà)出一個(gè)一維表格進(jìn)行分析。
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | |
| 走法 | 1 | 2 | 3 | 5 | 8 | 13 | 21 | 34 | 55 | 89 |
分析邊界條件:對(duì)于每個(gè)臺(tái)階每次上一級(jí)或者兩級(jí),當(dāng)臺(tái)階數(shù)為1時(shí)走法為1(即走一級(jí)即畢)為2時(shí)走法為2(走兩次一級(jí)和走一次二級(jí))。
分析遞歸關(guān)系:對(duì)于任一臺(tái)階都可以分為通過(guò)兩級(jí)或者一階到達(dá)。
終于,在有了上述兩個(gè)條件之后,問(wèn)題輕易得到了求解。(結(jié)合表格分析的手段到這里還沒(méi)有完全發(fā)揮出它該有的優(yōu)勢(shì),大家拭目以待)
臺(tái)階問(wèn)題基于c++的代碼實(shí)現(xiàn):
// ConsoleApplication2.cpp : 定義控制臺(tái)應(yīng)用程序的入口點(diǎn)。
//
//臺(tái)階問(wèn)題:有一座高度是10級(jí)臺(tái)階的樓梯,從下往上走,每跨一步只能向上1級(jí)或者2級(jí)臺(tái)階。要求用程序來(lái)求出一共有多少種走法。
#include "stdafx.h"
#include <iostream>
using namespace std;
int getResultByDP(int n)//自底向上的問(wèn)題解法
{
if (n<1)
{
return 0;
}
if (n==1)
{
return 1;
}
if (n==2)
{
return 2;
}
int a = 1;//從兩個(gè)遞歸基開(kāi)始
int b = 2;
int temp = 0;
for (int i = 3; i < n + 1; i++)
{
temp = a + b;
a = b;
b = temp;
}
return temp;
}
int _tmain(int argc, _TCHAR* argv[])
{
cout << getResultByDP(10);
system("pause");
return 0;
}
2、從矩陣左上角走到右下角最短路徑問(wèn)題
問(wèn)題描述:給定一個(gè)矩陣m,從左上角開(kāi)始每次只能向右走或者向下走,最后達(dá)到右下角的位置,路徑中所有數(shù)字累加起來(lái)就是路徑和,返回所有路徑的最小路徑和,如果給定的m如下,那么路徑1,3,1,0,6,1,0就是最小路徑和,返回12.
1 3 5 9
8 1 3 4
5 0 6 1
8 8 4 0
| 矩陣從左上角走到右下角 | |||||
| 0 | 1 | 2 | 3 | 4 | |
| 0 | 0 | 0 | 0 | 0 | 0 |
| 1 | 0 | 1 | 3 | 5 | 9 |
| 2 | 0 | 8 | 1 | 3 | 5 |
| 3 | 0 | 5 | 0 | 6 | 1 |
| 4 | 0 | 8 | 8 | 4 | 0 |
| 0 | 1 | 2 | 3 | 4 | |
| 0 | 0 | 0 | 0 | 0 | 0 |
| 1 | 0 | 1 | 4 | 9 | 18 |
| 2 | 0 | 9 | 5 | 8 | 13 |
| 3 | 0 | 14 | 5 | 11 | 12 |
| 4 | 0 | 22 | 13 | 15 | 12 |
邊界條件分析:由問(wèn)題知道對(duì)于任一矩陣中的元素而言,上次位置或者是在該元素的坐標(biāo)或者上邊。那么當(dāng)一些元素沒(méi)有左邊或者上邊時(shí)應(yīng)該怎么做呢?不妨就說(shuō)的更為具體一些吧,如上圖的表格所示當(dāng)x(表示行下標(biāo))等于1,和y(表示列下標(biāo))等于1時(shí)正好是對(duì)應(yīng)沒(méi)有上邊元素和沒(méi)有左邊元素的情況。對(duì)于只有左邊元素的值array[x][y]=array[x][y-1]+m[x][y],對(duì)于只有上邊元素:array[x][y]=array[x-1][y]+m[x][y](array為下面統(tǒng)計(jì)問(wèn)題結(jié)果的二維數(shù)組,m為包含輸入矩陣信息的二維數(shù)組)。
遞歸公式:對(duì)于平凡的子問(wèn)題而言 (推導(dǎo)遞歸公式時(shí)刻意的考察array[x][y]和array[x-1][y]與array[x][y-1]的實(shí)際關(guān)系)
對(duì)于此問(wèn)題而言:arry[x][y]=min(array[x-1][y],array[x][y-1])+m[x][y]
以下是該問(wèn)題基于c++的代碼實(shí)現(xiàn):
//給定一個(gè)矩陣m,從左上角開(kāi)始每次只能向右走或者向下走,最后達(dá)到右下角的位置,路徑中所有數(shù)字累加起來(lái)就是路徑和,返回所有路徑的最小路徑和,如果給定的m如下,那么路徑1,3,1,0,6,1,0就是最小路徑和,返回12.
#include "stdafx.h"
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
int const x_length=5, y_length=5;
int m[x_length][y_length] = {
0, 0, 0, 0, 0,
0, 1, 3, 5, 9,
0, 8, 1, 3, 5,
0, 5, 0, 6, 1,
0, 8, 8, 4, 0
};
int minDis() //m二級(jí)指針(可以是一個(gè)二維數(shù)組)
{
int dp[4 + 1][4 + 1];
//---------初始化邊界條件-----------------
for (size_t i = 0; i < x_length; i++)
{
dp[i][0] = 0;
}
for (size_t j = 0; j < y_length; j++)
{
dp[0][j] = 0;
}
//-------------------------------------------
for (size_t i = 1; i < x_length; i++)
{
for (size_t j= 1; j < y_length; j++)
{
if (i == 1)
{
dp[i][j] = dp[i][j - 1] + m[i][j];
}
else if (j == 1)
{
dp[i][j] = dp[i - 1][j] + m[i][j];
}
else
{
int temp1 = dp[i - 1][j] + m[i][j];
int temp2 = dp[i][j - 1] + m[i][j];
dp[i][j] = min(temp1, temp2);
}
}
}
return dp[x_length - 1][y_length - 1];
}
int _tmain(int argc, _TCHAR* argv[])
{
cout << "最右下角的最短路徑為:" << minDis();
system("pause");
return 0;
}
3、最大子數(shù)組問(wèn)題
問(wèn)題描述:給定數(shù)組arr,返回arr的最長(zhǎng)遞增子序列的長(zhǎng)度,比如arr=[2,1,5,3,6,4,8,9,7],最長(zhǎng)遞增子序列為[1,3,4,8,9]返回其長(zhǎng)度為5,由于該問(wèn)題中總要把當(dāng)前元素和在他之前的進(jìn)行分析,我們也是借助表格來(lái)直觀的分析該問(wèn)題。
| 2 | 4 | 5 | 3 | 1 | ||||
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | ||
| 2 | 0 | |||||||
| 4 | 1 | |||||||
| 5 | 2 | |||||||
| 3 | 3 | |||||||
| 1 | 4 | |||||||
| 0 | 1 | 2 | 3 | 4 | ||||
| 1 | 2 | 3 | 2 | 1 |
邊界條件:顯然對(duì)于第一個(gè)數(shù)而言有dp[0]=1(dp表示存放結(jié)果的數(shù)組)
遞歸公式:首先生成dp[n]的數(shù)組,dp[i]表示以必須arr[i]這個(gè)數(shù)結(jié)束的情況下產(chǎn)生的最大遞增子序列的長(zhǎng)度。對(duì)于第一個(gè)數(shù)來(lái)說(shuō),很明顯dp[0]為1,當(dāng)我們計(jì)算dp[i]的時(shí)候,我們?nèi)タ疾靑位置之前的所有位置,找到i位置之前的最大的dp值,記為dp[j](0=<j<i),dp[j]代表以arr[j]結(jié)尾的最長(zhǎng)遞增序列,而dp[j]又是之前計(jì)算過(guò)的最大的那個(gè)值,我們?cè)趤?lái)判斷arr[i]是否大于arr[j],如果大于dp[i]=dp[j]+1.計(jì)算完dp之后,我們找出dp中的最大值,即為這個(gè)串的最長(zhǎng)遞增序列。
該問(wèn)題基于c++的代碼實(shí)現(xiàn):
//給定數(shù)組arr,返回arr的最長(zhǎng)遞增子序列的長(zhǎng)度,比如arr=[2,1,5,3,6,4,8,9,7],最長(zhǎng)遞增子序列為[1,3,4,8,9]返回其長(zhǎng)度為5.
#include "stdafx.h"
#include <string>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int dp[5] = {};
int _tmain(int argc, _TCHAR* argv[])
{
int arr[5] = { 2, 4, 5, 3, 1 };
dp[0] = 1;
const int oo = 0;
for (int i = 1; i<5; i++){
int _max = oo;
for (int j = 0; j<i; j++)
if (dp[j]>_max && arr[i]>arr[j])
_max = dp[j];
dp[i] = _max + 1;
}
int maxlist = 0;
for (int i = 0; i < 5; i++)
if (dp[i] > maxlist)
maxlist = dp[i];
cout << maxlist << endl;
system("pause");
return 0;
}
4、最長(zhǎng)公共子序列
問(wèn)題描述:給定兩個(gè)字符串str1和str2,返回兩個(gè)字符串的最長(zhǎng)公共子序列,例如:str1="1A2C3D4B56",str2="B1D23CA45B6A","123456"和"12C4B6"都是最長(zhǎng)公共子序列,返回哪一個(gè)都行。
問(wèn)題分析:首先生成dp[n]的數(shù)組,dp[i]表示以必須arr[i]這個(gè)數(shù)結(jié)束的情況下產(chǎn)生的最大遞增子序列的長(zhǎng)度。對(duì)于第一個(gè)數(shù)來(lái)說(shuō),很明顯dp[0]為1,當(dāng)我們計(jì)算dp[i]的時(shí)候,我們?nèi)タ疾靑位置之前的所有位置,找到i位置之前的最大的dp值,記為dp[j](0=<j<i),dp[j]代表以arr[j]結(jié)尾的最長(zhǎng)遞增序列,而dp[j]又是之前計(jì)算過(guò)的最大的那個(gè)值,我們?cè)趤?lái)判斷arr[i]是否大于arr[j],如果大于dp[i]=dp[j]+1.計(jì)算完dp之后,我們找出dp中的最大值,即為這個(gè)串的最長(zhǎng)遞增序列。
| B | D | C | A | B | A | |||
| 0 | 1 | 2 | 3 | 4 | 5 | |||
| A | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| B | 1 | 0 | 0 | 0 | 0 | 1 | 1 | 1 |
| C | 2 | 0 | 1 | 1 | 1 | 1 | 2 | 2 |
| B | 3 | 0 | 1 | 1 | 2 | 2 | 2 | 2 |
| D | 4 | 0 | 1 | 1 | 2 | 2 | 3 | 3 |
| A | 5 | 0 | 1 | 2 | 2 | 2 | 3 | 3 |
| B | 6 | 0 | 1 | 2 | 2 | 3 | 3 | 4 |
| 7 | 0 | 1 | 2 | 2 | 3 | 4 | 4 | |
| B | D | C | A | B | A | |||
| 0 | 1 | 2 | 3 | 4 | 5 | |||
| A | 0 | -2 | -2 | -2 | -2 | -2 | -2 | -2 |
| B | 1 | -2 | -1 | -1 | -1 | 0 | 1 | 0 |
| C | 2 | -2 | 0 | 1 | 1 | -1 | 0 | 1 |
| B | 3 | -2 | -1 | -1 | 0 | 1 | -1 | -1 |
| D | 4 | -2 | 0 | -1 | -1 | -1 | 0 | 1 |
| A | 5 | -2 | -1 | 0 | -1 | -1 | -1 | -1 |
| B | 6 | -2 | -1 | -1 | -1 | 0 | -1 | 0 |
| 7 | -2 | 0 | -1 | -1 | -1 | 0 | -1 |
該問(wèn)題基于c++代碼實(shí)現(xiàn):
//輸入為兩個(gè)長(zhǎng)度不為零的字符串,輸出這兩個(gè)字符串的最大公共子序列
#include "stdafx.h"
#include <string>
#include <iostream>
#ifndef MAX
#define MAX(X,Y) ((X>=Y)? X:Y)
#endif
using namespace std;
int **Lcs_length(string X, string Y, int **B)
{
int x_len = X.length();
int y_len = Y.length();
int **C = new int *[x_len + 1];
for (int i = 0; i <= x_len; i++)
{
C[i] = new int[y_len + 1]; //定義一個(gè)存放最優(yōu)解的值的表;
}
for (int i = 0; i <= x_len; i++)
{
C[i][0] = 0;
B[i][0] = -2; //-2表示沒(méi)有方向
}
for (int j = 0; j <= y_len; j++)
{
C[0][j] = 0;
B[0][j] = -2;
}
for (int i = 1; i <= x_len; i++)
{
for (int j = 1; j <= y_len; j++)
{
if (X[i - 1] == Y[j - 1])
{
C[i][j] = C[i - 1][j - 1] + 1;
B[i][j] = 0; //0表示斜向左上
}
else
{
if (C[i - 1][j] >= C[i][j - 1])
{
C[i][j] = C[i - 1][j];
B[i][j] = -1; //-1表示豎直向上;
}
else
{
C[i][j] = C[i][j - 1];
B[i][j] = 1; //1表示橫向左
}
}
}
}
return C;
}
void OutPutLCS(int **B, string X, int str1_len, int str2_len)
{
if (str1_len == 0 || str2_len == 0)
{
return;
}
if (B[str1_len][str2_len] == 0) //箭頭斜向左上
{
OutPutLCS(B, X, str1_len - 1, str2_len - 1);
cout << X[str1_len - 1] << endl;
}
else if (B[str1_len][str2_len] == -1)
{
OutPutLCS(B, X, str1_len - 1, str2_len);
}
else
{
OutPutLCS(B, X, str1_len, str2_len - 1);
}
}
int _tmain(int argc, _TCHAR* argv[])
{
string X = "ABCBDAB";
string Y = "BDCABA";
int x_len = X.length();
int y_len = Y.length();
int **C;//定義一個(gè)二維數(shù)組
int **B = new int *[x_len + 1];
for (int i = 0; i <= x_len; i++)
{
B[i] = new int[y_len + 1];
}
C = Lcs_length(X, Y, B);
for (int i = 0; i <= x_len; i++)
{
for (int j = 0; j <= y_len; j++)
{
cout << C[i][j] << " ";
}
cout << endl;
}
cout << endl;
for (int i = 0; i <= x_len; i++)
{
for (int j = 0; j <= y_len; j++)
{
cout << B[i][j] << " ";
}
cout << endl;
}
OutPutLCS(B, X, x_len, y_len);//構(gòu)造最優(yōu)解
system("pause");
return 0;
}
到此這篇關(guān)于c++動(dòng)態(tài)規(guī)劃經(jīng)典算法的文章就介紹到這了,更多相關(guān)c++動(dòng)態(tài)規(guī)劃內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- C++中的動(dòng)態(tài)規(guī)劃子序列問(wèn)題分析探討
- C++動(dòng)態(tài)規(guī)劃計(jì)算最大子數(shù)組
- C++動(dòng)態(tài)規(guī)劃算法實(shí)現(xiàn)矩陣鏈乘法
- C++動(dòng)態(tài)規(guī)劃實(shí)現(xiàn)查找最長(zhǎng)公共子序列
- C++?動(dòng)態(tài)規(guī)劃算法使用分析
- C++編輯距離(動(dòng)態(tài)規(guī)劃)
- C++動(dòng)態(tài)規(guī)劃之最長(zhǎng)公子序列實(shí)例
- C++動(dòng)態(tài)規(guī)劃之背包問(wèn)題解決方法
- C++動(dòng)態(tài)規(guī)劃中關(guān)于背包問(wèn)題講解
相關(guān)文章
C++實(shí)現(xiàn)二叉樹(shù)的堂兄弟節(jié)點(diǎn)查詢(xún)
C++實(shí)現(xiàn)二叉樹(shù)的堂兄弟節(jié)點(diǎn)查詢(xún),是指在二叉樹(shù)中,找到兩個(gè)節(jié)點(diǎn)深度相同但父節(jié)點(diǎn)不同的節(jié)點(diǎn),即為堂兄弟節(jié)點(diǎn)。實(shí)現(xiàn)這一功能可以通過(guò)遍歷二叉樹(shù)并記錄節(jié)點(diǎn)深度和父節(jié)點(diǎn)來(lái)實(shí)現(xiàn)2023-04-04
淺析VSCode launch.json中的各種替換變量的意思 ${workspaceFolder} ${file} $
這篇文章主要介紹了VSCode launch.json中的各種替換變量的意思 ${workspaceFolder} ${file} ${fileBasename} ${fileDirname}等,非常不錯(cuò)具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-03-03
C/C++時(shí)間庫(kù)chrono的使用總結(jié)
std::chrono是C++標(biāo)準(zhǔn)庫(kù)中的一個(gè)組件,用于表示和處理時(shí)間,其功能就像是心理學(xué)中的感知系統(tǒng),它可以為我們捕捉、量化并操作抽象的時(shí)間概念,這就如同我們的大腦可以理解和感知周?chē)h(huán)境的時(shí)間流逝一樣,這種感知和理解能力是人類(lèi)進(jìn)行日?;顒?dòng)所必需的,2023-12-12
C++動(dòng)態(tài)規(guī)劃計(jì)算最大子數(shù)組
所謂最大子數(shù)組就是連續(xù)的若干數(shù)組元素,如果其和是最大的,那么這個(gè)子數(shù)組就稱(chēng)為該數(shù)組的最大子數(shù)組2022-06-06
C++語(yǔ)法中的函數(shù)重載和默認(rèn)參數(shù)
這篇文章主要介紹了C++語(yǔ)法中的函數(shù)重載和默認(rèn)參數(shù),本文從語(yǔ)法角度通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-03-03

