0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

base case和备忘录初始值怎么定?

jf_78858299 来源:labuladong 作者:labuladong 2023-04-19 10:28 次阅读

很多读者对动态规划问题的 base case、备忘录初始值等问题存在疑问。

本文就专门讲一讲这类问题,顺便聊一聊怎么通过题目的蛛丝马迹揣测出题人的小心思,辅助我们解题。

看下力扣第 931 题「下降路径最小和」,输入为一个n * n的二维数组matrix,请你计算从第一行落到最后一行,经过的路径和最小为多少。

函数签名如下:

int minFallingPathSum(int[][] matrix);

就是说你可以站在matrix的第一行的任意一个元素,需要下降到最后一行。

每次下降,可以向下、向左下、向右下三个方向移动一格。也就是说,可以从matrix[i][j]降到matrix[i+1][j]matrix[i+1][j-1]matrix[i+1][j+1]三个位置。

请你计算下降的「最小路径和」,比如说题目给了一个例子:

图片

今天这道题也是类似的,不算是困难的题目,所以 我们借这道题来讲讲 base case 的返回值、备忘录的初始值、索引越界情况的返回值如何确定

不过还是要通过 动态规划的标准套路介绍一下这道题的解题思路,首先我们可以定义一个dp数组:

int dp(int[][] matrix, int i, int j);

这个dp函数的含义如下:

从第一行(matrix[0][..])向下落,落到位置matrix[i][j]的最小路径和为dp(matrix, i, j)

根据这个定义,我们可以把主函数的逻辑写出来:

public int minFallingPathSum(int[][] matrix) {
    int n = matrix.length;
    int res = Integer.MAX_VALUE;

    // 终点可能在最后一行的任意一列
    for (int j = 0; j < n; j++) {
        res = Math.min(res, dp(matrix, n - 1, j));
    }

    return res;
}

因为我们可能落到最后一行的任意一列,所以要穷举一下,看看落到哪一列才能得到最小的路径和。

接下来看看dp函数如何实现。

对于matrix[i][j],只有可能从matrix[i-1][j], matrix[i-1][j-1], matrix[i-1][j+1]这三个位置转移过来。

图片

那么,只要知道到达(i-1, j), (i-1, j-1), (i-1, j+1)这三个位置的最小路径和,加上matrix[i][j]的值,就能够计算出来到达位置(i, j)的最小路径和

int dp(int[][] matrix, int i, int j) {
    // 非法索引检查
    if (i < 0 || j < 0 ||
        i >= matrix.length ||
        j >= matrix[0].length) {
        // 返回一个特殊值
        return 99999;
    }
    // base case
    if (i == 0) {
        return matrix[i][j];
    }
    // 状态转移
    return matrix[i][j] + min(
            dp(matrix, i - 1, j), 
            dp(matrix, i - 1, j - 1),
            dp(matrix, i - 1, j + 1)
        );
}

int min(int a, int b, int c) {
    return Math.min(a, Math.min(b, c));
}

当然,上述代码是暴力穷举解法,我们可以用备忘录的方法消除重叠子问题,完整代码如下:

public int minFallingPathSum(int[][] matrix) {
    int n = matrix.length;
    int res = Integer.MAX_VALUE;
    // 备忘录里的值初始化为 66666
    memo = new int[n][n];
    for (int i = 0; i < n; i++) {
        Arrays.fill(memo[i], 66666);
    }
    // 终点可能在 matrix[n-1] 的任意一列
    for (int j = 0; j < n; j++) {
        res = Math.min(res, dp(matrix, n - 1, j));
    }
    return res;
}

// 备忘录
int[][] memo;

int dp(int[][] matrix, int i, int j) {
    // 1、索引合法性检查
    if (i < 0 || j < 0 ||
        i >= matrix.length ||
        j >= matrix[0].length) {

        return 99999;
    }
    // 2、base case
    if (i == 0) {
        return matrix[0][j];
    }
    // 3、查找备忘录,防止重复计算
    if (memo[i][j] != 66666) {
        return memo[i][j];
    }
    // 进行状态转移
    memo[i][j] = matrix[i][j] + min(
            dp(matrix, i - 1, j), 
            dp(matrix, i - 1, j - 1),
            dp(matrix, i - 1, j + 1)
        );

    return memo[i][j];
}

int min(int a, int b, int c) {
    return Math.min(a, Math.min(b, c));
}

如果看过我们公众号之前的动态规划系列文章,这个解题思路应该是非常容易理解的。

那么本文对于这个dp函数仔细探讨三个问题

1、对于索引的合法性检测,返回值为什么是 99999?其他的值行不行?

2、base case 为什么是i == 0

3、备忘录memo的初始值为什么是 66666?其他值行不行?

首先,说说 base case 为什么是i == 0,返回值为什么是matrix[0][j],这是根据dp函数的定义所决定的

回顾我们的dp函数定义:

从第一行(matrix[0][..])向下落,落到位置matrix[i][j]的最小路径和为dp(matrix, i, j)

根据这个定义,我们就是从matrix[0][j]开始下落。那如果我们想落到的目的地就是i == 0,所需的路径和当然就是matrix[0][j]呗。

再说说备忘录memo的初始值为什么是 66666,这是由题目给出的数据范围决定的

备忘录memo数组的作用是什么?

就是防止重复计算,将dp(matrix, i, j)的计算结果存进memo[i][j],遇到重复计算可以直接返回。

那么,我们必须要知道memo[i][j]到底存储计算结果没有,对吧?如果存结果了,就直接返回;没存,就去递归计算。

所以,memo的初始值一定得是特殊值,和合法的答案有所区分。

我们回过头看看题目给出的数据范围:

matrixn * n的二维数组,其中1 <= n <= 100;对于二维数组中的元素,有-100 <= matrix[i][j] <= 100

假设matrix的大小是 100 x 100,所有元素都是 100,那么从第一行往下落,得到的路径和就是 100 x 100 = 10000,也就是最大的合法答案。

类似的,依然假设matrix的大小是 100 x 100,所有元素是 -100,那么从第一行往下落,就得到了最小的合法答案 -100 x 100 = -10000。

也就是说,这个问题的合法结果会落在区间[-10000, 10000]中。

所以,我们memo的初始值就要避开区间[-10000, 10000],换句话说,memo的初始值只要在区间(-inf, -10001] U [10001, +inf)中就可以。

最后,说说对于不合法的索引,返回值应该如何确定,这需要根据我们状态转移方程的逻辑确定

对于这道题,状态转移的基本逻辑如下:

int dp(int[][] matrix, int i, int j) {

    return matrix[i][j] + min(
            dp(matrix, i - 1, j), 
            dp(matrix, i - 1, j - 1),
            dp(matrix, i - 1, j + 1)
        );
}

显然,i - 1, j - 1, j + 1这几个运算可能会造成索引越界,对于索引越界的dp函数,应该返回一个不可能被取到的值。

因为我们调用的是min函数,最终返回的值是最小值,所以对于不合法的索引,只要dp函数返回一个永远不会被取到的最大值即可。

刚才说了,合法答案的区间是[-10000, 10000],所以我们的返回值只要大于 10000 就相当于一个永不会取到的最大值。

换句话说,只要返回区间[10001, +inf)中的一个值,就能保证不会被取到。

至此,我们就把动态规划相关的三个细节问题举例说明了。

拓展延伸一下,建议大家做题时,除了题意本身,一定不要忽视题目给定的其他信息

本文举的例子,测试用例数据范围可以确定「什么是特殊值」,从而帮助我们将思路转化成代码。

除此之外,数据范围还可以帮我们估算算法的时间/空间复杂度。

比如说,有的算法题,你只想到一个暴力求解思路,时间复杂度比较高。如果发现题目给定的数据量比较大,那么肯定可以说明这个求解思路有问题或者存在优化的空间。

除了数据范围,有时候题目还会限制我们算法的时间复杂度,这种信息其实也暗示着一些东西。

比如要求我们的算法复杂度是O(NlogN),你想想怎么才能搞出一个对数级别的复杂度呢?

肯定得用到 二分搜索或者二叉树相关的数据结构,比如TreeMapPriorityQueue之类的对吧。

再比如,有时候题目要求你的算法时间复杂度是O(MN),这可以联想到什么?

可以大胆猜测,常规解法是用 [回溯算法暴力穷举,但是更好的解法是动态规划,而且是一个二维动态规划,需要一个M * N的二维dp数组,所以产生了这样一个时间复杂度。

如果你早就胸有成竹了,那就当我没说,毕竟猜测也不一定准确;但如果你本来就没啥解题思路,那有了这些推测之后,最起码可以给你的思路一些方向吧?

总之,多动脑筋,不放过任何蛛丝马迹,你不成为刷题小能手才怪。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Base
    +关注

    关注

    0

    文章

    11

    浏览量

    8708
  • 数组
    +关注

    关注

    1

    文章

    417

    浏览量

    25939
  • 动态规划算法

    关注

    0

    文章

    6

    浏览量

    1625
收藏 人收藏

    评论

    相关推荐

    恩智浦与Cohda无线签署CAR 2 CAR通信联盟“谅解备忘录

    恩智浦半导体和Cohda的无线今天宣布,他们已经签署的CAR 2 CAR通信联盟”谅解备忘录”(MOU)。该备忘录旨在确保欧洲车与车之间,或是汽车和交通基础设施间,无线通讯科技技术的实施和统一
    发表于 04-17 10:10 921次阅读

    HarmonyOS开发实例:【手机备忘录

    基于用户首选项,实现了备忘录新增、更新、删除以及查找等功能。
    的头像 发表于 04-18 21:40 791次阅读
    HarmonyOS开发实例:【手机<b class='flag-5'>备忘录</b>】

    PostgreSQL操作备忘录

    PostgreSQL 操作备忘录
    发表于 05-23 08:48

    UDS诊断命令备忘录

    UDS实践性强,逻辑复杂,很多服务非要体验过一次才能理解,导致包括我在内的初学者感觉晦涩难懂,不明觉厉,因此将自己的理解写下来、整理下来,与君共勉。零、UDS诊断命令备忘录一、简介UDS
    发表于 08-26 16:09

    怎样去搭建一种基于XR806的开源桌面备忘录

    本人计划怼一个开源桌面备忘录/天气预报/相册的项目基于XR806,同时学习鸿蒙操作系统获得晕哥赠送的开发板和芯片,目前处于环境搭建阶段看起来这个芯片玩的人比较少,目前遇到了问题,不知道如何解决,希望
    发表于 12-28 06:52

    换路定律及初始值的确定

    换路定律及初始值的确定:3.2 换路定律及初始值的确定3.2.1 换路定律通常,我们把电路中开关的接通、断开或电路参数的突然变化等统称为“换路”。我们研究的是换路后电
    发表于 05-10 00:04 30次下载

    全球半导体联盟与中国半导体行业签署合作备忘录

    全球半导体联盟与中国半导体行业签署合作备忘录 全球半导体联盟(GSA)与中国半导体行业协会(CSIA)在苏州联合申明签署合作备忘录。此次合作将为促
    发表于 09-24 08:17 698次阅读

    是德科技与中国移动签署谅解备忘录

    是德科技(NYSE:KEYS)今日宣布与中国移动通信集团有限公司(CMCC)签署谅解备忘录(MoU)将全力支持 5G 终端先行者计划的实施。
    的头像 发表于 07-19 11:01 4838次阅读

    戴姆勒与百度签署谅解备忘录

    7月25日,奔驰母公司戴姆勒与百度签署谅解备忘录,深化双方在自动驾驶和车联网等领域的战略合作。
    的头像 发表于 07-28 09:53 2719次阅读

    Vedanta与30家日本公司签署谅解备忘录

    印度Vedanta Group已与30家日本公司签署谅解备忘录,以开发印度半导体和玻璃显示器制造生态系统。上周在日本东京举行的2022年Vedanta-Avanstrate商业合作伙伴峰会上签署了这些备忘录,来自100多家全球公司的200多名代表出席了峰会。
    的头像 发表于 12-15 09:12 974次阅读

    设计模式:备忘录设计模式

    备忘录设计模式(Memento Design Pattern)是一种行为型设计模式,它的主要目的是在不破坏对象封装性的前提下,捕捉和保存一个对象的内部状态
    的头像 发表于 06-06 11:19 799次阅读

    设计模式行为型:备忘录模式

    备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对象。备忘录模式属于行为型模式。
    的头像 发表于 06-07 11:16 853次阅读
    设计模式行为型:<b class='flag-5'>备忘录</b>模式

    新思科技同越南政府签署谅解备忘录

    在越南总理范明政访美期间,新思科技与越南国家创新中心(nic)签署了关于培养越南集成电路设计人才的谅解备忘录,支持nic成立芯片设计孵化中心。另外,新思科技与越南信息通讯部下属的信息通信技术产业公司签订了支援越南半导体产业发展的谅解备忘录
    的头像 发表于 09-20 10:56 1553次阅读

    实践GoF的23种设计模式:备忘录模式

    相对于代理模式、工厂模式等设计模式,备忘录模式(Memento)在我们日常开发中出镜率并不高,除了应用场景的限制之外,另一个原因,可能是备忘录模式
    的头像 发表于 11-25 09:05 544次阅读
    实践GoF的23种设计模式:<b class='flag-5'>备忘录</b>模式

    苹果iOS 18将支持语音备忘录及数学符号显示

    首先是语音备忘录功能。据悉,苹果有意在iOS 18系统中加入此项功能,使iPhone用户能够便捷地录制音频文件,并将其直接嵌入至备忘录之中。
    的头像 发表于 04-18 11:14 519次阅读