概述
首博
今天尝试了一下动态规划思想的代码实现,选取了比较简单的数塔问题,下面展示的是写这个代码遇到的问题。(附有c++源程序)
问题是这样的:有如下所示的数塔,要求从顶层走到底层,若每一步只能走到相邻的结点,则经过的结点的数字之和最大是多少?
思路分析:
运用贪婪算法大概率无法得到最优解,原因在于当前的最优解并不一定是最终最优路径的走向。
此类问题应用动态规划算法(dp)可以很好的解决。
动态规划的思想是求子问题的最优解,关键是找到递推公式。用递推公式推出最优解,用回溯的方法输出得到最优解的路径。
动态规划的递推公式均类似这种形式:max( f(n), f ( n+1)) + data;
这种递推与递归不同,基于循环实现,比较好理解。
对于数塔的算法,是从底部向上渐渐求得最优解,所谓底部,就是选取问题的一个起始单位,这个单位可以用一种可复制的方式带入递推公式把整个问题遍历结束。对于数塔这个特殊的模型,只能从底部开始。
在算法开始之前,需要把数塔抽象为计算机模型,很显然,我们可以把它看成下三角矩阵或上三角矩阵(但是上三角矩阵不符合平时的阅读喜欢和思维模式,因此使用下三角矩阵),矩阵用二维数组实现。对于一个节点向下延伸的两个节点,则变成了a[i+1][j]和a[i+1][j+1]。这一点很重要,在程序里面,每个节点需要比较max的也就是这两个节点,且对于j的取值也是通过这个限定的。
在程序中,我们需要定义两个全局的二维数组,data[][]用于存储数塔的数据,dp[][]用于存储变化过程。且数组dp还具备回溯时的定位和判断。
注意:
1.矩阵下标和数组下标差1,在for循环的结束位置需要注意。
2.行i是递减的,且用于计算的位置是最下面一行-1,列j是递增的,且大小不超过i的大小。
3.最终结果就保存在了data[0][0]中。
4.自己将data表和dp表提前画出来,每一步都对照表的内容编程,这样可以减少边界错误。
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1000;
int data[N][N];
int dp[N][N]; //过程储存量,记录整个决策过程
int n;
void Tower(){
int j = 0;
//为dp初始化,最下层注入data的起始数值
for(int j = 0; j < n; j++){
dp[n-1][j] = data[n-1][j];
}
//开始逐层决策
//int tm;
for(int i = n-1; i >= 0; i--){ //i,j到底从哪开始到哪结束,需要仔细斟酌
for(int j = 0; j <= i; j++){
dp[i][j] = max(dp[i+1][j], dp[i+1][j+1]) + data[i][j]; //j最后也得+1,因此最大值和i同步即可 !!!比较的是dp的内容而不是data的内容
}
}
//测试dp数组值
cout<<"测试路径值的数塔:"<<endl;
for(int i = 0; i < n; i++){
for(int j = 0; j <= i; j++){
cout<<dp[i][j]<<"t";
}
cout<<"n";
}
}
void Print(){
int j = 0;
int value = 0;
cout<<"数塔的路径最大和为:"<<dp[0][0]<<endl;
cout<<"此路径为:"<<data[0][0];
for(int i = 1; i < n; i++){
value = dp[i-1][j] - data[i-1][j];
while(dp[i][j] != value){
j++;
}
cout<<"-->"<<data[i][j];
}
cout<<"n"<<endl;
}
int main(){
cout<<"请输入数塔层数:"<<endl;
cin>>n;
cout<<"n请输入这个数塔:n"<<endl;
for(int i = 0; i < n; i++)
for(int j = 0; j <= i; j++){
cin>>data[i][j];
}
Tower();
Print();
}
运行结果:
这是完全按照动态规划最基本的定义写的,至于动态数组等优化暂时没有给出。
这类题的重点在于递推公式的使用,难点就是判断出一道题是用动态规划完成的。
最后
以上就是霸气火龙果为你收集整理的算法笔记--动态规划--数塔问题的全部内容,希望文章能够帮你解决算法笔记--动态规划--数塔问题所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复