为什么不用递归:
因为递归进行计算的时候会产生较多的重复计算,例如求斐波那契数列fib(8),
中间的fib(5),fib(4),fib(3),fib(2)等重复计算了好多次,导致不必要的计算,当然可以用一个数组来储存计算过的值,这样也是一种优化思路。
动态规划的主要的思路是先建模,建立一个金字塔模型用于分析问题,确定状态。然后从下往上计算,用一个数组储存计算过的底层的最优解,最后数组最后一个值就是总问题的最优解。
例题1:
找出两个字符串的公共子序列。
思路:首先需要一个二维数组用来存储底层的状态值,每次计算是否相同的时候还应该考虑前一个已经计算的值,来决定这个值到底是什么状态。
代码:
#include<iostream>
#include<string>
using namespace std;
char s1[4] = "asd";
char s2[4] = "ase";
int Max_str(int length1, int length2){
int exit_[length1+1][length2+2];
for(int i = 0; i<=length1; i++){
exit_[i][0] = 0;
}
for(int j = 0; j<=length2; j++){
exit_[0][j] = 0;
};
for(int i = 1;i<=length1;i++){
for(int j = 1;j<=length2; j++){
if(s1[i-1] == s2[j-1]){
exit_[i][j] = exit_[i-1][j-1]+1;
}
else{
int A = exit_[i-1][j];
int B = exit_[i][j-1];
exit_[i][j] = A>B?A:B;
}
}
}
return exit_[length1][length2];
}
int main(){
cout<<sizeof(s1)<<endl;
cout<<sizeof(s2)<<endl;
cout<<Max_str(sizeof(s1),sizeof(s2))<<endl;
return 0;
}
例题二:
数字三角形(POJ1163)
在上面的数字三角形中寻找一条从顶部到底边的路径,使得路径上所经过的数字之和最大。路径上的每一步都只能往左下或 右下走。只需要求出这个最大和即可,不必给出具体路径。 三角形的行数大于1小于等于100,数字为 0 - 99。
递归思路:
三角形用一份二维矩阵存储,不断往下面递归,返回两个相加和较大的一个。
代码:
#include<iostream>
using namespace std;
int temp[5][5];
int D[100][100];
int Maxpath(int i,int j){
if(i == 4 ){
return D[i][j];
}
else{
int A = Maxpath(i+1,j);
int B = Maxpath(i+1,j+1);
return (A>B?A:B)+D[i][j];
}
// return temp[i][j];
};
int main(){
int temp1[] = {7,3,8,8,1,0,2,7,4,4,4,5,2,6,5};
int cout = 0;
for(int i = 0; i<5; ++i){
for(int j = 0;j<=i ; ++j){
D[i][j] = temp1[cout++];
}
}
std::cout << Maxpath(0, 0) << std::endl;
}
这边用递归在解决子问题的时候产生了多个不必要的重复计算,所以效率不是太高。
动态规划思路:
分析这种问题一般是从上到下的建模分析问题,从下到上的解决问题。现在是从二维矩阵的最下面一层开始进行向上运算,我们可以用原来的二维矩阵储存已经计算过的值。
代码:
#include<iostream>
using namespace std;
int temp[5][5];
int D[100][100];
int main(){
int temp1[] = {7,3,8,8,1,0,2,7,4,4,4,5,2,6,5};
int cout = 0;
for(int i = 0; i<5; ++i){
for(int j = 0;j<=i ; ++j){
D[i][j] = temp1[cout++];
}
}
int A,B;
for(int i = 4; i>=0;i--){
for(int j = i; j>=0; j--){
if(i == 4){
}
else{
A = D[i+1][j]+D[i][j];
B = D[i+1][j+1]+D[i][j];
D[i][j] = A>B?A:B;
}
}
}
std::cout << D[0][0] << std::endl;
return 0;
}
最长公共子序列问题:
求两个字符串的最长公共子序列(最长公共子序列可以是不连续的,最长公共子串是连续的)。
思路分析:这是一道经典的动态规划的题,我们从两个字符串的最后面进行分析,如果两个字符串的最后一个字母是相同的,那么这个字母就是公共子序列里面的最后一份值,如果最后两个不相同,那么就是比较去掉最后一个字母的字符串A和另外一个字符串B的比较公共子序列的值,或者是去掉最后一个字符的字符串B和另外一个完整的字符串A比较公共子序列。这就是主体思路。
动态规划法实现思路:找个一二维数组从两个字符串的开头进行遍历,若两个字符串相同,则数组储存值在前一个相同的基础上面加一,否则就储存前一个不同值里面的最大值(也就是数组位置里面的上面和左面的最大值)。最后返回数组右下角储存的值就是最长公共子序列的长度。