【欧拉计划】Q12:数塔问题

数塔问题


  • 递推与递归
  • 动态规划

1.onlineJ43:数塔问题

  • 原题链接:http://oj.haizeix.com/problem/43

有一个由数字组成的三角形数塔,站在上一层的某个点,只能到达其下方左右的两个点。现在请找到一条从上到下的路径,使得路径上所有数字相加之和最大

image-20210417201534724

1.递推法从上向下求和
  1. 由数塔中到达任意一点的数字和sum = 左上方数字和suml or 右上方数字和sumr + 该数字数值num
  2. 知到达该点最大和为ans[x][y] = max(ans[x - 1][y - 1], ans[x - 1][y]) + num[x][y]关键
  3. 遍历最后一行,找出最大的数字即为结果

image-20210418110447750

#include<iostream>
#include<cstdio>
using namespace std;

int n;
int num[1005][1005];

int main(){
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i){
        for(int j = 1; j <= i; ++j){
            scanf("%d", &num[i][j]);
        }
    }
    //1.从两个方向取数值大的方向max(num[i - 1][j - 1], num[i - 1][j]);
    for(int i = 1; i <= n; ++i){
        for(int j = 1; j <= i; ++j){
            num[i][j] += max(num[i - 1][j - 1], num[i - 1][j]);
        }
    }
    int ans = 0;
    //2.遍历最后一行,找出最大的数字即为结果
    for(int i = 1; i <= n; ++i){
        ans = max(ans, num[n][i]);
    }
    printf("%d\n", ans);
    return 0;
}
2.递推法从下向上求和
  1. 由数塔中到达任意一点的数字和sum = 左下方数字和suml or 右下方数字和sumr + 该数字数值num,
  2. 知到达该点最大和为num[x][y] = max(num[x + 1][y], num[x + 1][y + 1]) + num[x][y]关键
  3. 对整个数塔处理后,最大值即为(1, 1)点处的数值

image-20210418110523588

#include<iostream>
#include<cstdio>
using namespace std;

int n, num[1005][1005];

int main(){
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i){
        for(int j = 1; j <= i; ++j){
            scanf("%d", &num[i][j]);
        }
    }
    //1.从两个方向取数值大的方向max(num[x + 1][y], num[x + 1][y + 1]);
    for(int i = n; i > 0; --i){
        for(int j = 1; j <= i; ++j){
            num[i][j] += max(num[i + 1][j], num[i + 1][j + 1]);
        }
    }
    //2.对整个数塔处理后,最大值即为(1, 1)点处的数值
    printf("%d\n", num[1][1]);
    return 0;
}

2.onlineJ590:数塔狂想曲

如下图是一个数塔,映射到该数塔上行走的规则为:从左上角的点开始,向下走或向右下走直到最底层结束。

1
3 8
2 5 0
1 4 3 8
1 4 2 5 0

路径最大和是 1+8+5+4+4=221+8+5+4+4=22,1+8+5+3+5=221+8+5+3+5=22 或者 1+8+0+8+5=221+8+0+8+5=22

小 S 觉得这个问题 so easy,于是他提高了点难度,他每次 ban 掉一个点,然后询问你不走该点的最大路径和。当然他上一个询问被 ban 掉的点过一个询问就会恢复。

如果被 ban 掉后不存在任意一条路径,则输出 −1。

1.利用最大值、次大值输出预处理答案
  1. 利用数塔问题的两种解法,从上向下和、从下向上和可以求出经过某点(x, y)最大数字和数组
  2. 找到每一行的最大值(记录下标)、次大值(记录数值)
  3. 对于某一次询问(x, y),判断被ban的值是否为最大值;如果不是输出最大值,否则直接输出次大值

总述:将数塔输入并存储在二维数组中,利用数塔问题的两种解法将绿色、蓝色二维数组求出

求出黑色二维数组(经过某点所能获得的最大和),最后通过记录最大值、次大值来求出结果

image-20210418153351294

#include<iostream>
#include<cstdio>
using namespace std;

//n:数塔行数、m:询问次数、utd:从上向下求解的数组、dtu:从下向上求解的数组、ans:经过某点最大和数组
int n, m, num[1005][1005], utd[1005][1005], dtu[1005][1005], ans[1005][1005];
//mmax记录最大值、mmax_ind记录最大值下标、mmax2记录次大值
int mmax[1005], mmax_ind[1005], mmax2[1005];

int main(){
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; ++i){
        for(int j = 1; j <= i; ++j){
            scanf("%d", &num[i][j]);
        }
    }
    //1.利用数塔问题的两种解法,从上向下和、从下向上和可以求出经过某点(x, y)最大数字和数组
    //(1)从上向下求和,并存入数组utd中
    for(int i = 1; i <= n; ++i){
        for(int j = 1; j <= i; ++j){
            utd[i][j] = max(utd[i - 1][j - 1], utd[i - 1][j]) + num[i][j];
        }
    }
    //(2)从下向上求和,并存入数组dtu中
    for(int i = n; i > 0; --i){
        for(int j = 1; j <= i; ++j){
            dtu[i][j] = max(dtu[i + 1][j], dtu[i + 1][j + 1]) + num[i][j];
        }
    }
    //(3)通过从上向下和、从下向上和可以求出经过某点(x, y)最大数字和ans数组
    for(int i = 1; i <= n; ++i){
        for(int j = 1; j <= i; ++j){
            ans[i][j] = utd[i][j] + dtu[i][j] - num[i][j];
        }
    }
    //2.找到每一行的最大值(记录数值、下标)、次大值(记录数值)
    for(int i = 2; i <= n; ++i){
        //定义临时变量m、m2、ind
        int m = 0, m2 = 0, ind = 0;
        for(int j = 1; j <= i; ++j){
            if(m < ans[i][j]){
                //(1)遍历第i行记录第i行的最大值和下标
                m2 = m;//原来的最大值赋值给次大值
                m = ans[i][j];
                ind = j;
            }else if(m2 < ans[i][j]){
                //(2)遍历第i行记录第i行的次大值
                m2 = ans[i][j];
            }
        }
        mmax[i] = m, mmax_ind[i] = ind, mmax2[i] = m2;
    }
    //3.对于某一次询问(x, y),判断被ban的值是否为最大值;如果不是输出最大值,否则直接输出次大值
    for(int i = 0; i < m; ++i){
        int x, y;
        scanf("%d%d", &x, &y);
        if(x == 1){
            printf("-1\n");
        }else if(mmax_ind[x] == y){
            //(1)x行的最大值被ban,输出x行的次大值mmax2
            printf("%d\n", mmax2[x]);
        }else{
            //(2)x行的最大值未被ban,输出x行的最大值mmax
            printf("%d\n", mmax[x]);
        }
    }
    return 0;
}

tips:文章部分内容参考算法刷题课程,题解图示内容及代码根据老师课程、以及自己对知识的理解,进行二次整理和部分补充,仅供学习参考使用,不可商业化。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值