算法分析例题02 数塔问题 动态规划

有形如下图的一个数塔,从顶部出发,在每一结点可以选择向左走或是向右走,一直走到底层,要求找出一条路径,使路径上的数值和最大。

在这里插入图片描述
分析:贪心算法不适用于此题,因为每一层的选择会影响到其下一层的选择,子解是相互影响的,很喜欢老师上课讲的一句话:在这题中我们不应该盲目地去取每一层的最优解,而是携带着先前的状态去做当前层的决策。这很好地讲述了动态规划问题的精髓,我们由下而上的解决问题时,一要保存子问题的状态,二要利用子问题的状态解决当前问题。

法一:自上而下的动态规划
在这种动态规划中,起始点唯一,终点为n
设置初始条件O(1) 取解O(n)
由于数塔结构需要考虑边界条件

#include <iostream>
#include <algorithm>
#define n 5

using namespace std;

int nums[n][n] = {
    {9},
    {12, 5},
    {10, 6, 8},
    {2, 18, 9, 5},
    {19, 7, 10, 4, 16}
};

int main() {
    int dp[n][n];
    int res = 0;
    dp[0][0] = nums[0][0];	// 初始条件:第一层只有一个节点 直接保存到dp
    for (int i = 1; i < n; i++) 
        for (int j = 0; j <= i; j++) 
            if (j == 0 || j == i)	// 边界条件:从塔尖到塔边路径唯一
                dp[i][j] = (j == 0 ? dp[i - 1][j] : dp[i - 1][j - 1]) +  nums[i][j];
            else	// 状态转移:其余节点挑选来源的两条路径的较大值
                dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - 1]) + nums[i][j];

    for (int j = 0; j < n; j++)	// 遍历出口取最大值
        if (dp[n - 1][j] > res)
            res = dp[n - 1][j];
    
    cout << "max:" << res << endl;	// 输出最大数值:59
    system("pause");
    return 0;
}

法二:自下而上的动态规划
在这种动态规划中,起始点为n,终点唯一
设置初始条件O(n) 取解O(1)
不需要考虑边界条件

#include <iostream>
#include <algorithm>
#define n 5

using namespace std;

int nums[n][n] = {
    {9},
    {12, 5},
    {10, 6, 8},
    {2, 18, 9, 5},
    {19, 7, 10, 4, 16}
};

int main() {
    int dp[n][n];
    int res = 0;

    for (int j = 0; j < n; j++)	// 初始条件:将底层的每个出发点 直接保存到dp数组
        if (dp[n - 1][j] > res)
            dp[n - 1][j] = nums[n - 1][j];
    
    for (int i = n - 2; i >= 0; i--)	// 边界条件:无
        for (int j = 0; j <= i; j++) // 状态转移:其余节点挑选来源的两条路径的较大值
                dp[i][j] = max(dp[i + 1][j], dp[i + 1][j + 1]) + nums[i][j];

    res = dp[0][0];	// 终点唯一
    cout << "max:" << res << endl;
    system("pause");
    return 0;
}

总结:
①如需比较到达不同终点状态的各个路径时,使用顺序法较好。
②如需知道塔中每一点到最下层的最大值时,使用逆序法较好。

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值