数字三角形【(暴搜,记忆化,DP,倒序DP)+解释边界初始化】

51 篇文章 0 订阅

😊😊 😊😊
不求点赞,只求耐心看完,指出您的疑惑和写的不好的地方,谢谢您。本人会及时更正感谢。希望看完后能帮助您理解算法的本质
😊😊 😊😊

[USACO1.5][IOI1994]数字三角形 Number Triangles

题目描述

观察下面的数字金字塔。

写一个程序来查找从最高点到底部任意处结束的路径,使路径经过数字的和最大。每一步可以走到左下方的点也可以到达右下方的点。

        7 
      3   8 
    8   1   0 
  2   7   4   4 
4   5   2   6   5 

在上面的样例中,从 7 → 3 → 8 → 7 → 5 7 \to 3 \to 8 \to 7 \to 5 73875 的路径产生了最大

输入格式

第一个行一个正整数 r r r ,表示行的数目。

后面每行为这个数字金字塔特定行包含的整数。

输出格式

单独的一行,包含那个可能得到的最大的和。

样例 #1

样例输入 #1

5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5

样例输出 #1

30

提示

【数据范围】
对于 100 % 100\% 100% 的数据, 1 ≤ r ≤ 1000 1\le r \le 1000 1r1000,所有输入在 [ 0 , 100 ] [0,100] [0,100] 范围内。

题目翻译来自NOCOW。

USACO Training Section 1.5

IOI1994 Day1T1

小白到进阶各种解法:

一、暴搜:😊

在这里插入图片描述

思路:

枚举所有从第一层到最后一层的路径,当最后一层走完的时候,与之前所有路径的最大值,进行比较然后取最值。

代码:

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;
const int N = 1e3 + 10;
int g[N][N];    //三角形矩阵本身!
int n, res;

void dfs (int x, int y, int sum)
{
    if (x > n){
        res = max(res, sum);
        return;
    }
    
    // 搜索
    dfs (x+1, y, sum + g[x+1][y]);
    dfs (x+1, y+1, sum + g[x+1][y+1]);
    return ;
}
 
int main()
{
    cin >> n;
    
    //输入数字三角形矩阵!
    for (int i=1; i <= n; i ++)
        for (int j=1; j <= i; j ++)
            cin >> g[i][j];
    dfs (1, 1, g[1][1]); //从起点开始往下搜路径
    cout << res << endl;
    return 0;
}

在这里插入图片描述

二、记忆化搜索:😊

在这里插入图片描述

思路:

对于已经走过的点进行记录,即记录当前点到目标点的最大和值,下次再走到这个点的时候也会是一样的结果,所以直接返回,不用再递归搜索当前点的分支了,剪枝返回!

  1. 从起点出发开始搜索。
  2. 因为每个点可以有两个方向走,但是不知道哪个方向路径总和更大,所以两个方向之间取一个 m a x max max 值。
  3. 如何具体化一条路径的最大值呢?显然只有走到底部的时候,即整条路的走法已经确定了,回溯累加答案!

代码:

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;
const int N = 1e3 + 10;
int g[N][N];    //三角形矩阵本身!
int n, res;
int f[N][N];    //记录从点 (i, j)到达最后一层的和值。

int dfs (int x, int y)
{
    if (f[x][y] != -1)
        return f[x][y];
    if (x == n){
        return f[x][y] = g[x][y];
    }
    
    // 搜索
    f[x][y] = max(dfs(x+1,y), dfs(x+1, y+1)) + g[x][y];
    
    return f[x][y];
}
 
int main()
{
    cin >> n;
    memset (f, -1, sizeof(f));
    //输入数字三角形矩阵!
    for (int i=1; i <= n; i ++)
        for (int j=1; j <= i; j ++)
            cin >> g[i][j];
    cout << dfs(1, 1);
    return 0;
}

在这里插入图片描述

三、本题考察算法:从上到下DP – 初始化😊

即从子问题推导大问题,和记忆化搜索相比的话, D P DP DP 更类似于从从小到大,而记忆化搜索是回溯的时候更新答案。

思路:

为什么从上到下要初始化呢?
请看下图:思考红色圈是由哪些状态转移而来的!
在这里插入图片描述
由于本题的输入不是一个规则输入,而是直角三角形的形式输入的,所以说对于边界上的点,转移状态可能取到为空,有人说,空的不是初始值为0吗?为何会初始化一个负无穷大呢?因为本题的数据也可能是负数,所以只能初始化为负无穷,从而保证状态的转移正确!

代码:

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;
const int N = 1e3 + 10;
int g[N][N];
int f[N][N];    //从起点到当前点的最大权值和!
int n;

int main()
{
    cin >> n;
    for (int i=1; i <=n ; i ++)
        for (int j=1; j <= i; j ++)
            cin >> g[i][j];
    memset (f, -0x3f, sizeof(f));  //为什么不赋初值为0呢?因为g[i][j]有可能为负数
    f[1][1] = g[1][1];
    for (int i=2; i <= n; i ++)
    {
        for (int j=1; j <= i; j ++)
        {
            f[i][j] = max(f[i-1][j], f[i-1][j-1]) + g[i][j];
        }
    }
    int res = -1e8;
    for (int i=1; i <= n; i ++)
        res= max(res, f[n][i]);
    cout << res << endl;
    return 0;
}

在这里插入图片描述

四、本题考察算法:从下到上DP – 无初始化😊

思路:

在这里插入图片描述
由图可知,我们从下往上转移状态的时候,针对于其中的边界上的点,其状态的转移是不会出现越界的!所以无需初始化!
目标求: m a x ( f [ 1 ] [ 1 ] ) max(f[1][1]) max(f[1][1])

但是有同学会问:那我们代码里从第n层递推过来的啊,且递推公式如下:

 f[i][j] = max(f[i+1][j], f[i+1][j+1]) + g[i][j];

f [ i + 1 ] , f [ j + 1 ] f[i+1],f[j+1] f[i+1]f[j+1] 不就越界了吗?为什么不赋值为负无穷呢?
答:就是赋值为0,请注意: m a x 后面的 g [ i ] [ j ] max后面的 g[i][j] max后面的g[i][j],只有 g [ i ] [ j ] g[i][j] g[i][j] 的存在,当 f [ i + 1 ] [ j ] , f [ i + 1 ] [ j + 1 ] f[i+1][j], f[i+1][j+1] f[i+1][j],f[i+1][j+1]都取0时,此时最后一行就等于初值!是符合题意和正确的答案!

代码:

#include<iostream>
#include<cstring>

using namespace std;
const int N = 1e3 + 10;
int f[N][N];
int g[N][N];
int n;

int main()
{
    cin >> n;
    for (int i=1; i <= n; i ++)
        for (int j=1; j <= i; j ++)
            cin >> g[i][j];
    
    //从下往上递推!
    for (int i=n; i>=1; i--)
        for (int j=1; j<=i; j++)
            f[i][j] = max(f[i+1][j], f[i+1][j+1]) + g[i][j];
    cout << f[1][1] << endl;
    return 0;
}

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值