动态规划:数塔问题

动态规划问题我训练过一些题目,但是感觉自己掌握的还不是特别好!

下面以一道经典的动态规划题目说明动态规划算法的思想,文末会官方的给出对动态规划的文字叙述。

先看题目:如下图(图片来自百度图片)是一个数塔,从顶部出发在每一个节点可以选择向左或者向右走,一直走到底层,要求找出一条路径,使得路径上的数字之和最大.

思路分析:

这道题目如果使用贪婪算法不能保证找到真正的最大和。

在用动态规划考虑数塔问题时可以自顶向下的分析,自底向上的计算。

从顶点出发时到底向左走还是向右走应取决于是从左走能取到最大值还是从右走能取到最大值,只要左右两道路径上的最大值求出来了才能作出决策。同样的道理下一层的走向又要取决于再下一层上的最大值是否已经求出才能决策。这样一层一层推下去,直到倒数第二层时就非常明了。

所以第一步对第五层的8个数据,做如下四次决策:

如果经过第四层2,则在第五层的19和7中肯定是19;

如果经过第四层18,则在第五层的7和10中肯定是10;

如果经过第四层9,则在第五层的10和4中肯定是10;

如果经过第四层5,则在第五层的4和16中肯定是16;

经过一次决策,问题降了一阶。5层数塔问题转换成4层数塔问题,如此循环决策…… 最后得到1阶的数塔问题。

算法实现:首先利用一个二维数组data存储数塔的原始数据(其实我们只使用数组data一半的空间,一个下三角矩阵),然后利用一个中间数组dp存储每一次决策过程中的结果(也是一个下三角矩阵)。

初始化dp,将data的最后一层拷贝到dp中。dp[n][j] = data[n][j] (j = 1, 2, …, n) 其中,n为数塔的层数。

在动态规划过程汇总,我们有dp[i][j] = max(dp[i+1][j], dp[i+1][j+1]) + data[i][j],最后的结果保存在dp[0][0]中。

对于上面的数塔,我们的data数组如下:

9
12 15
10 6 8
2 18 9 5
19 7 10 4 16

而我们的dp数组如下:

59
50 49
38 34 29
21 28 19 21
19 7 10 4 16

下面是C++代码的实现(Visual Studio2013通过):

#include <iostream>
#include <algorithm>

using namespace std;

/************************************************************************/
/* 数塔问题                                                               */
/************************************************************************/
const int N = 50;//为了算法写起来简单,这里定义一个足够大的数用来存储数据(为了避免运算过程中动态申请空间,这样的话算法看起来比较麻烦,这里只是为了算法看起来简单)
int data[N][N];//存储数塔原始数据
int dp[N][N];//存储动态规划过程中的数据
int n;//塔的层数

/*动态规划实现数塔求解*/
void tower_walk()
{
    // dp初始化
    for (int i = 0; i < n; ++i)
    {
        dp[n - 1][i] = data[n - 1][i];
    }
    int temp_max;
    for (int i = n - 1; i >= 0; --i)
    {
        for (int j = 0; j <= i; ++j)
        {
            // 使用递推公式计算dp的值
            temp_max = max(dp[i + 1][j], dp[i + 1][j + 1]);
            dp[i][j] = temp_max + data[i][j];
        }
    }
}

/*打印最终结果*/
void print_result()
{
    cout << "最大路径和:" << dp[0][0] << ‘\n‘;
    int node_value;
    // 首先输出塔顶元素
    cout << "最大路径:" << data[0][0];
    int j = 0;
    for (int i = 1; i < n; ++i)
    {
        node_value = dp[i - 1][j] - data[i - 1][j];
        /* 如果node_value == dp[i][j]则说明下一步应该是data[i][j];如果node_value == dp[i][j + 1]则说明下一步应该是data[i][j + 1]*/
        if (node_value == dp[i][j + 1]) ++j;
        cout << "->" << data[i][j];
    }
    cout << endl;
}

int main()
{
    cout << "输入塔的层数:";
    cin >> n;
    cout << "输入塔的节点数据(第i层有i个节点):\n";
    for (int i = 0; i < n; ++i)
    {
        for (int j = 0; j <= i; ++j)
        {
            cin >> data[i][j];
        }
    }

    tower_walk();
    print_result();
}

运行结果:

上面的算法是按照最原始的思路进行书写的,其实还可以进行优化,这里不做详细说明。



下面官方的叙述下什么是动态规划:

动态规划(dynamic programming)是运筹学的一个分支,是求解决策过程(decision process)最优化的数学方法。20世纪50年代初美国数学家R.E.Bellman等人在研究多阶段决策过程(multistep decision process)的优化问题时,提出了著名的最优化原理(principle of optimality),把多阶段过程转化为一系列单阶段问题,利用各阶段之间的关系,逐个求解,创立了解决这类过程优化问题的新方法——动态规划。动态规划(dynamic programming)是运筹学的一个分支,是求解决策过程(decision process)最优化的数学方法。20世纪50年代初美国数学家R.E.Bellman等人在研究多阶段决策过程(multistep decision process)的优化问题时,提出了著名的最优化原理(principle of optimality),把多阶段过程转化为一系列单阶段问题,利用各阶段之间的关系,逐个求解,创立了解决这类过程优化问题的新方法——动态规划。(摘自百度百科)

动态规划处理的对象是针对多阶段决策问题。多阶段决策问题是指这样的一类特殊的活动过程:问题可以分解成若干个相互联系的阶段,在每一个阶段都要做出决策,形成一个决策序列,该决策序列也称为一个策略。每次决策依赖于当前状态,又随即引起状态的转移,决策序列(策略)都是在变化的状态中产生出来的,故有“动态”的含义。所以这种多阶段最优化决策解决问题过程称为动态规划。

对于每一个决策序列,可以在满足问题的约束条件下用一个数值函数(即目标函数)来衡量该策略的优劣。多阶段决策问题的最优化目标是获取导致问题最优值得最优决策序列,即得到最优解。(摘自《算法设计方法与优化》滕国文等编著)

个人感觉动态规划算法的解决重要的是首先必须有能力将实际问题识别为动态规划问题,然后是找出优化过程中的递推关系式,这样就比较容易了。

时间: 2024-10-29 19:10:00

动态规划:数塔问题的相关文章

动态规划——数塔问题

从数塔顶层出发,每个结点可以选择向左走或向右走,要求一直走到塔底,使得走过的路径上的数值和最大. #include <iostream> #include <cstdio> using namespace std; const int N = 100; // 下面这个函数实现的是更新最大值,o赋值为o和x的最大值 template <class T> void updateMax(T& o, const T& x) { o = (o > x) ?

动态规划----数塔问题

一.问题描述 从数塔顶层出发,每个结点可以选择向左走或向右走,要求一直走到塔底,使得走过的路径上的数值和最大. 如上图所示的数塔,最大路径和为86,经过的路径从塔顶到塔底为13,8,26,15,24. 二.问题分析 要求的最大值可以从地形开始也可以从上面开始 动态规划函数为: resultTower[i][j] = tower[i][j] + Math.max(tower[i + 1][j],tower[i + 1][j + 1]); 边界值resultTower[heigh - 1][j] =

动态规划&amp;数塔取数&amp;矩阵取数&amp;背包问题&amp;最大子段和&amp;正整数分组

动态规划(dynamic programming)是运筹学的一个分支,是求解决策过程(decision process)最优化的数学方法.在面试笔试中动态规划也是经常作为考题出现,其中较为简单的DP题目我们应该有百分之百的把握顺利解决才可以. 一.动态规划定义动态规划实际上是一类题目的总称,并不是指某个固定的算法.动态规划的意义就是通过采用递推(或者分而治之)的策略,通过解决大问题的子问题从而解决整体的做法.动态规划的核心思想是巧妙地将问题拆分成多个子问题,通过计算子问题而得到整体问题的解.而子

G - 免费馅饼 HDU 1176 (动态规划---数塔的变形 )

G - 免费馅饼 Time Limit:1000MS Memory Limit:32768KB 64bit IO Format:%I64d & %I64u Submit Status Practice HDU 1176 Description 都说天上不会掉馅饼,但有一天gameboy正走在回家的小径上,忽然天上掉下大把大把的馅饼.说来gameboy的人品实在是太好了,这馅饼别处都不掉,就掉落在他身旁的10米范围内.馅饼如果掉在了地上当然就不能吃了,所以gameboy马上卸下身上的背包去接.但由

hdu2084动态规划入门题----数塔

原题:数塔 这个是动态规划入门题,比较简单. 题意是: 一个数字组成的三角形,从上到下找一条路径,使这条路径上数字之和最大. 解题思路,就是要从下往上看.举个例子: 如果你从上到下走到了第4行第1个数,也就是2,那么接下来有两个数可以走19和7,而你必然会选择19. 所以就可以根据这个思路更新上面一行的数.把2更新成2+19=21.18更新成18+10=28,9更新成9+10=19,5更新成5+16=21 重复上面的思路最后第一行累加出来的就是最大值了. 思路很简单,最简单的实现就是你也开一个二

hdu 2084 数塔 dp 动态规划

开始动态规划的学习了,先是比较基础的,很金典的数塔.附上题目链接  http://acm.hdu.edu.cn/showproblem.php?pid=2084 这题的状态转移方程是  dp[i][j] = max(dp[i-1][j-1],dp[i-1][j]) + m[i][j]; (dp[i][j] 表示在第 i 层 第 j 列时的最大和) . 然后一个双重循环,边能算出.当然可以用滚动数组.但是注意用滚动数组解题时,第二层循环 j 必须从大到小, 因为 状态转移方程 为  f  [ j

c++动态规划解决数塔问题

#include <iostream>#include <algorithm> using namespace std; /************************************************************************//* 数塔问题 *//************************************************************************/const int N = 50;//为了算法写

hdu2084 数塔 动态规划第二题

数塔 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 25898    Accepted Submission(s): 15621 Problem Description 在讲述DP算法的时候,一个经典的例子就是数塔问题,它是这样描述的: 有如下所示的数塔,要求从顶层走到底层,若每一步只能走到相邻的结点,则经过的结点的数字之和最大是多少

HDU 2084 数塔 简单动态规划

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2084 题目大意:有如下所示的数塔,要求从顶层走到底层,若每一步只能走到相邻的结点,则经过的结点的数字之和最大是多少? 题目分析:首先我们可以给数塔中的每一个点制定一个坐标.最上面的9的坐标是(1,1),第二行的12的坐标是(2,1),15的坐标是(2,2),以此类推,第i行的第j个数的坐标就是(i,j).我们假设一共有n行,并且设a[i][j]为坐标为(i,j)对应的数,f[i][j]为从(i,j)