51nod_1002_数塔取数问题

题目

原题链接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1002
题目描述:
一个高度为N的由正整数组成的三角形,从上走到下,求经过的数字和的最大值。
每次只能走到下一层相邻的数上,例如从第3层的6向下走,只能走到第4层的2或9上。

5
8 4
3 6 9
7 2 9 5

例子中的最优方案是:5 + 8 + 6 + 9 = 28

Input:
第1行:N,N为数塔的高度。(2 <= N <= 500)
第2 - N + 1行:每行包括1层数塔的数字,第2行1个数,第3行2个数……第k+1行k个数。
数与数之间用空格分隔(0 <= A[i] <= 105) 。
Output:
输出最大值

解题思路

自底向上

从上往下走,只有两种选择,所以所有可能的路的条数可以用一个二叉树来表示。如下图:

img

图1 路径的所有可能数

从图1很容易的能够看出来,要想整个路径达到最大,就要5加上它左右节点到叶子节点较大的一个,
即 res = 5 + max(left, right)。由此我们就可以递归地求解。

res f(root) {
    if (leaf)
    return leaf;
    else
    return root + max(left, right);
}

但是我们可以发现,在整个递归过程中,重复计算了很多节点。如图6中,节点6出现了
两次,层数越多,重复计算的就越多,会导致指数级的时间复杂度。
我们可以用额外的空间来保存已经计算过的子树的最大值,当再次访问到相同的节点时,
就不用继续递归,直接返回值。

res fun(root) {
    if (leaf)
    return leaf;
    if (root in the table)
    return max of root;
    else {
    table[root] = root + max(left, right);
    return table[root];
    }
}

自顶向下

这种思路就是从上往下,计算出顶部到达某一层每个节点的路径长度,保存到数组中,
然后以此层路径长度计算下一层路径长度,直到最底层。最后顶层到所有叶子节点的
最长路径都计算出来,只需找到其中的最大值就行。

代码

自底向上

#include <stdio.h>
#include <stdlib.h>

typedef int DataType; 

DataType data[505][505];//原数据
DataType table[505][505];//记录计算的值

int n = 0;

DataType max(DataType a, DataType b) {
    return a > b ? a : b;
}


DataType dp(int i, int j) {
    if (i > n) {
    return 0;
    }
    if (table[i][j] != 0) {
    return table[i][j];
    } else {
    table[i][j] =  max(dp(i + 1, j), dp(i + 1, j + 1)) + data[i][j];
    return table[i][j];
    }
}


int main() {
    scanf("%d", &n);
    int i, j;
    for(i = 2;i <= n;i++) {
    for(j = 1;j <= i;j++) {
        scanf("%d", &data[i][j]); 
    }
    }
    DataType res = dp(0, 0);
    printf("%d\n", res);
    return 0;
}

自顶向下

#include <stdio.h>
#include <stdlib.h>
typedef int DataType; 



DataType max(DataType a, DataType b) {
    return a > b ? a : b;
}


DataType dp[505];

int main() {
    int n;
    scanf("%d", &n);
    scanf("%d", dp + 1);//第一层

    int i, j, x, m;
    for (i = 2;i <= n;i++) {
    DataType a = 0;
    for (j = 1;j <= i;j++) {
        scanf("%d", &x);
        m = max(dp[j], dp[j-1]) + x;// 根据上一层计算到达x节点的最大值
        dp[j-1] = a;// 更新dp[j-1],此时dp[j-1]保存的是当前层对应节点的最大值
        a = m;
    }
    dp[i] = a; //当前层的最后一个节点
    }

    // 遍历保存的值,找出最大
    DataType res = dp[1];
    for (i = 2;i <= n;i++) {
    if (res < dp[i]) {
        res = dp[i];
    }
    }
    printf("%d\n", res);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值