让三岁小孩都能理解动态规划_来自B站罐装-蜜糖

news2024/9/24 21:26:01

系列文章目录


文章目录

  • 系列文章目录
  • 一、认识算法
    • 动态规划难在哪?
    • 学习目标
  • 二、记忆化搜索 非常直觉的处理方式
    • 注意:
  • 三、70.爬楼梯 入门 模板
    • 通过记忆化搜索 发现动态规划四要素
  • 四、118.杨辉三角 使用答案空间处理(题目给了返回值的样式)
  • 五、198.打家劫舍 记忆化搜索转化
  • 六、279.完全平方数 背包问题
  • 七、322.零钱兑换 背包问题
  • 八、139.单词拆分


一、认识算法

不同于二分查找和堆排序,这种有明确步骤的算法,用一个不太恰当的例子,就像我们在做菜的时候,一般算法就像是先放两克盐,再放两克鸡精,动态规划更像是说先加盐少许,再加鸡精少许,最后达到好吃即可。也是将大问题拆分成小问题。

动态规划难在哪?

  1. 没有一个统一解的算法思想,不容易学透。
  2. 不同方法间的难度差距很大

学习目标

一月入门,二月上手
结合目标,以退为进
如果不是为了进大厂,不需要掌握很好,掌握基本题目即可。

二、记忆化搜索 非常直觉的处理方式

在这里插入图片描述

  1. 首先初始化一个保存记忆搜索内容的缓存,将它初始化成一些数字,这里为“-1”。
  2. 然后在我们执行递归的方法中,往往我们一上来先判断终止条件,这里模板上的终止条件是小于等于1,这时候我们就返回默认值。
  3. 然后判断是否命中记忆缓存,如果命中,直接返回缓存。 然后执行我们的状态转移方程,这里状态转移方程是dp[n]=dp[n-1]+dp[n-2]
  4. 之后更新缓存,并返回结果。

注意:

题目如果足够简单就不用了。
因为使用了系统栈,速度较慢,可能会超时。
当我们完成了记忆化搜索的动态规划后,我们可以根据现在实现的逻辑,将其改为使用矩阵的状态转移动态规划。下面用具体例子练习。

三、70.爬楼梯 入门 模板

在这里插入图片描述
解题思路:
我们可以想到对于爬到第n个台阶,它有两种情况被爬到,第一种从第n-1阶台阶爬1个台阶,第二种从第n-2阶台阶爬2个台阶。所以它被爬到的不同方法,是这两种情况的总和。并且我们使用记忆化搜索(比较符合直觉)。代码如下所示:

class Solution {
   
HashMap<Integer,Integer> map = new HashMap<>();

public int climbStairs(int n){
    if(n == 1){
        return 1;
    }
    if(n == 2){
        return 2;
    }
    if(map.containsKey(n)){return map.get(n);}//记忆力搜索。
    int res =climbStairs(n-1)+ climbStairs(n-2);
    map.put(n,res);
    return res;
    }
}

如果我们按照从下向上的方式上台阶的话,从0和1的位置都只有一种答案,那我们就从0和1开始填写,填写过程:2=1+0,3=1+2,4=3+2(此处数字表示位置)我们可以看出可以通过for循环完成对数组的填写,代码如下:
在这里插入图片描述

通过记忆化搜索 发现动态规划四要素

  1. 状态类型(前缀的、坐标的、区间的)
  2. 转移方程 比如res =climbStairs(n-1)+ climbStairs(n-2);
  3. 数据初始化
  4. 答案位置 比如本体最终答案在n上。

四、118.杨辉三角 使用答案空间处理(题目给了返回值的样式)

在这里插入图片描述

class Solution {
    public List<List<Integer>> generate(int numRows) {
        List<List<Integer>> res = new ArrayList<>();
        List<Integer> one = new ArrayList<>();
        one.add(1);
        res.add(one);
        if(numRows ==1){
            return res;
        }
        List<Integer> two = new ArrayList<>();
        two.add(1);
        two.add(1);
        res.add(two);
        if(numRows ==2){return res;}
        for(int i=2;i<numRows ;i++){
            List<Integer> line = new ArrayList<>();
            line.add(1);
            List<Integer> last =res.get(i-1);
            for(int j=0;j<i-1;j++){
                line.add(last.get(j)+ last.get(j+1));
            }
            line.add(1);
            res.add(line);
        }
    return res;
    }
}

五、198.打家劫舍 记忆化搜索转化

在这里插入图片描述
我们对一个位置的处理是否是正确的,我们只需要对比两种情况,选这个位置或者不选这个位置。
选了这个位置,相当于位置n的值与与上一个位置n-2的最优值相加,再不选这个位置的情况下,则我们相当于直接选了上一个位置n-1的最优值。

class Solution {
    public int rob(int[] nums) {
        if(nums.length ==0){return 0 ;}
        if(nums.length ==1){return nums[0];}
        int n = nums.length;
        int[] dp = new int[n];
        dp[0]= nums[0];
        dp[1] = Math.max(nums[1], nums[0]);
        for(int i=2;i<n;i++){
            dp[i]= Math.max(dp[i-2]+ nums[i],dp[i -1]);
        }
        return dp[n-1];
    }
}

六、279.完全平方数 背包问题

在这里插入图片描述
我们取的完全平方数得范围在 [1, 根号n ]之间,
我们先对更好理解的但是超时的答案代码进行分析,其超时答案代码如下:

class Solution {
    public int numSquares(int n) {
        ArrayList<Integer> list = new ArrayList<>();
        for( int i =1;i*i<=n;i++){
            list.add(i * i);
        }
        int len = list.size();
        int[][] dp = new int[len][n+1];
        for(int i =0;i<len;i++){
            Arrays.fill(dp[i], -1);
        }
        return process(list, 0, n, dp); 
    }
    private int process(List<Integer> list, int index, int rest, int[][] dp){
        if(rest == 0){
            return 0;
        }
        if(index == list.size()){
            return Integer.MAX_VALUE;
        }
        if(dp[index][rest] != -1){
            return dp[index][rest];
        }
        int curr = list.get(index);
        int res = Integer.MAX_VALUE;
        for(int i =0;curr *i<=rest;i++){
            final int process = process(list, index + 1, rest - curr * i,dp);
            if(process != Integer.MAX_VALUE){
                res = Math.min(res, i+process);
            }
        }
        dp[index][rest]= res;
        return res;
    }
}

为了存放完全平方数,我们需要执行如下代码:

ArrayList<Integer> list = new ArrayList<>();
for( int i =1;i*i<=n;i++){
     list.add(i * i);
}

为了实现记忆化搜索,我们需要执行如下代码:我们为每个完全平方数,设置了一个n+1长度的数组,也就是可以存放等于n和比n小的所有整数a所对应的一个数字,这个数字就是这个a可以被最少个数的完全平方数表示的个数。这里我们用“-1”来初始化,等于“-1”表明此时这个位置没有存储记忆。

int len = list.size();
int[][] dp = new int[len][n+1];
for(int i =0;i<len;i++){
     Arrays.fill(dp[i], -1);
}

我们再看process这个函数,代码如下,首先rest==0,说明了输入的n已经完全被完全平方数所替代,不需要用新的完全平方数来替代了,所以返回0。index == list.size()时候,这个index如果作为下标,已经越界,需要被终止。dp[index][rest] != -1说明这里存在记忆,可以进行调用。 int curr = list.get(index);是用来找到index所对应的完全平方数,然后for循环是用来将rest依次减去一个完全平方数,两个完全平方数,更多完全平方数,因为一个大的完全平方数可以由几个小的完全平方数所合成,所以由大的完全平方数构成的n所使用的完全平方数个数更少,也就更符合解题目的。代码整体思路是,先把n用小的完全平方数表示出来,他的完全平方数个数肯定比较大,然后再用更大的表示,使用的完全平方数的个数会减小。

private int process(List<Integer> list, int index, int rest, int[][] dp){
        if(rest == 0){
            return 0;
        }
        if(index == list.size()){
            return Integer.MAX_VALUE;
        }
        if(dp[index][rest] != -1){
            return dp[index][rest];
        }
        int curr = list.get(index);
        int res = Integer.MAX_VALUE;
        for(int i =0;curr *i<=rest;i++){
            final int process = process(list, index + 1, rest - curr * i,dp);
            if(process != Integer.MAX_VALUE){
                res = Math.min(res, i+process);
            }
        }
        dp[index][rest]= res;
        return res;
    }

但是超时了,所以记忆化搜索在某些情况下会超时,所以我们需要用状态矩阵的方式再实现一边。因为我们是要找到最小选取数量, 我们可以借用第70.题爬楼梯的思想,对于组成输入n的最少完全平方数个数,可以由它前面的结果得来,只不过爬楼梯是求种类数,是相加,而现在是取最小值Math.min(dp[i+nn[j]], dp[i] + 1)。代码如下:

class Solution {
public int numSquares(int n){
    int nlen = (int)Math.sqrt(n)+ 1;
    int[] nn = new int[nlen];
    for(int i=0;i<nlen; i++){
        nn[i]=(i +1)*(i + 1);
    }
    int[] dp = new int[n+1];
    Arrays.fill(dp, Integer.MAX_VALUE);
    dp[0]=0;
    for(int i=0;i<n; i++){
        for(int j=0;j<nlen; j++){
            if(i + nn[j] <= n){
                dp[i + nn[j]]= Math.min(dp[i+nn[j]], dp[i] + 1);
            }
        }
    }
    return dp[n];
}
}

七、322.零钱兑换 背包问题

在这里插入图片描述
思路与完全平方数相同,代码如下:

class Solution {
    public int coinChange(int[] coins, int amount) {
        if(coins.length == 0){
            return -1;
        }
        int [] dp = new int[amount +1];
        return process(coins,amount,dp);

    }
    private int process(int[] coins,int amount,int[] dp){
        if(amount < 0){
            return -1;
        }
        if(dp[amount] !=0){
            return dp[amount];
        }
        if(amount == 0){
            return 0;
        }
        int res = Integer.MAX_VALUE;
        for(int i =0;i<coins.length;i++){
            if(coins[i]<=amount){
                final int p1 = process(coins, amount - coins[i], dp);
                if(p1!= -1){
                    res = Math.min(res, p1+1);
                }
            }
        }
        res = res == Integer.MAX_VALUE ? -1 : res;
        dp[amount] = res;
        return res;
    }
}

代码改进:此时状态空间为剩余的金额,我们可以在这个状态空间中进行状态的维护,如下图所示,如果硬币面值为“1”,我们就可以根据1个长度之前的最好结果来更新状态,或者“5”同理。
在这里插入图片描述
所以我们在创建了状态空间之后,就可以针对所有硬币的面额尽心循环,然后从有效的位置开始直接便利所有的结果空间,并记录最优情况。注意没有有效结果的话,需要返回“-1”。
在这里插入图片描述

八、139.单词拆分

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1989360.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

The Ether: EvilScience (v1.0.1)打靶渗透【附代码】(权限提升)

靶机下载地址: https://www.vulnhub.com/entry/the-ether-evilscience-v101,212/ 1. 主机发现端口扫描目录扫描敏感信息获取 1.1. 主机发现 nmap -sn 192.168.7.0/24|grep -B 2 00:0C:29:7F:50:FB 1.2. 端口扫描 nmap -p- 192.168.7.172 1.3. 目录扫描 dirb http://192.16…

vm虚拟机下安装CentOS7系统

VMware16安装CentOS7 1.启动之前安装的VM 具体VMware安装过程 2.配置Linux&#xff08;centos7&#xff09;的镜像文件 选择安装镜像文件 4.开启虚拟机 开始读秒安装 选择安装过程中使用的语言&#xff0c;这里选择英文、键盘选择美式键盘。点击Continue 首先设置时间…

MySQL——数据类型、索引的建立、数据的约束

文章目录 数据类型索引的建立普通索引唯一索引使用ALTER 命令添加和删除索引使用ALTER 命令添加和删除主键显示索引信息 数据的约束非空约束&#xff1a;not null&#xff0c;值不能为null唯一约束&#xff1a;unique&#xff0c;值不能重复主键约束&#xff1a;primary key外键…

【实现100个unity特效之15】最简单的方法使用shader graphs实现2d非像素和像素树叶草的随风摇摆效果

文章目录 前言非像素树叶草飘动效果新建材质效果像素树叶草飘动效果参考完结 前言 本文只是实现一个简单版本的2d树叶草随风摇摆的效果&#xff0c;如果你想要实现更加复杂的效果&#xff0c;包括2d互动草&#xff0c;你可以参考我之前的文章&#xff1a; 【推荐100个unity插件…

基元检测-霍夫直线变换原理

在之前已经使用过正规方程法、梯度下降法拟合直线 1、回顾过去 梯度下降法拟合一元函数 最小二乘法的线性代数推导 现在使用一种新的直线检测方法&#xff0c;霍夫变换&#xff0c;它也可以拟合直线&#xff0c;接下里将说明他的原理。 2、霍夫变换 霍夫变换&#xff08;…

工地安全巡检系统

在建筑施工行业&#xff0c;安全始终是高悬的“达摩克利斯之剑”。随着科技的日新月异&#xff0c;工地安全二维码巡检系统崭露头角&#xff0c;成为守护工地安全的强大利器。 通过凡尔码平台&#xff0c;工地安全巡检系统融合了先进信息技术与安全管理理念的创新成果。其功能丰…

免费的录屏软件有哪些?试试这4款巨好用的录屏软件

4个免费简单实用的录屏软件推荐&#xff0c;帮你解决录屏问题。 1、转转大师录屏软件 下载链接>>screencap.55.la 这是一款非会员录屏也不限制时长的免费录屏软件&#xff0c;操作简单&#xff0c;第一次用就会&#xff0c;必须五星推荐给你&#xff01; 支持多种录制模…

数据结构——排序(1):插入排序

目录 一、排序的概念 二、排列的运用 三、常见的排序算法 四、插入排序 1.直接插入排序 &#xff08;1&#xff09;思路 &#xff08;2&#xff09;过程图示 &#xff08;3&#xff09;代码实现 (4)代码解释 &#xff08;5&#xff09;特性 2.希尔排序 &#xff08;1…

【Java算法专场】位运算(上)

目录 常见位运算总结 位1的个数 算法思路 算法代码 比特位计数 算法思路 算法代码 汉明距离 算法思路 算法代码 只出现一次的数字 算法思路 算法代码 丢失的数字 算法思路 算法代码 常见位运算总结 了解位运算的一些基本操作&#xff0c;那么我们就来通过题目来…

STM32的USB接口介绍

STM32 USB接口是STM32微控制器系列中集成的一种通信接口&#xff0c;它允许STM32微控制器与外部设备或计算机进行高速的数据传输和通信。以下是STM32 USB接口的简要介绍&#xff1a; 1. 接口类型 STM32的USB接口通常支持USB 2.0标准&#xff0c;部分高端型号可能还支持USB 3.…

新手必看!剪映轻松上手,让你的视频瞬间高大上

相信现在短视频兴起的时代下&#xff0c;几乎人手都在学习如何剪辑出日常视频&#xff0c;尤其是想要走新媒体路线的小伙伴更是在尝试专业的剪辑&#xff0c;不过平时的vlog或者抖音短视频可以从简单的开始接触&#xff0c;剪映是其中的一款适合初学者上手的剪辑了&#xff0c;…

网络编程复习

1.网络编程基础 1.1引入 socket套接字实现主机之间的通信 cs通信模型基于socket实现&#xff0c;需要客户端软件来实现通信 bs通信模型基于http实现&#xff0c;是网页通信&#xff0c;不需要任何客户端软件 1.2通信协议 &#xff08;1&#xff09;OSI七层通信协议&#xff…

QEMU理解与分析系列(1):QEMU简介

QEMU简介 一、QEMU基本介绍1.1操作模式1.2 虚拟化方式中间代码实现方式简介源码结构分布 二、qemu tcg前端解码逻辑2.1 tcg翻译流程2.1.1 decode tree语法2.1.2 trans_xxx函数的逻辑 三、编译相关3.1 代码拉取&#xff08;拉取自己想要的版本&#xff09;3.2 编译参数3.3 依赖包…

Spring Boot - 在Spring Boot中实现灵活的API版本控制(上)

文章目录 为什么需要多版本管理&#xff1f;在Spring Boot中实现多版本API的常用方法1. URL路径中包含版本号2. 请求头中包含版本号3. 自定义注解和拦截器 注意事项 为什么需要多版本管理&#xff1f; API接口的多版本管理在我们日常的开发中很重要&#xff0c;特别是当API需要…

关于Zoho mail邮箱续费、退款、升级的说明

在使用企业邮箱服务的过程中&#xff0c;可能会遇到续费&#xff0c;退款及升级服务的情况。遇到这些情况时应该如何处理&#xff1f;本文将为您提供遇到邮箱情况时的详细操作步骤&#xff1a;邮箱续费方式及续费流程、邮箱申请退款方式、邮箱升级服务的流程。 一、Zoho邮箱如…

动态面板门槛模型及 Stata 具体操作步骤

目录 一、文献综述 二、理论原理 三、实证模型 四、稳健性检验 五、程序代码及解释 六、代码运行结果 一、文献综述 动态面板门槛模型作为一种先进的计量经济学方法&#xff0c;在众多领域的研究中发挥着关键作用。在经济增长领域&#xff0c;[学者 A]通过构建动态面板门槛…

你的会议记录还用手写吗?3款免费语音转文字工具

现在&#xff0c;随着大家越来越习惯用电脑和手机工作&#xff0c;很多专业人士都在找更快捷的方法来记录会议内容。以前那种手写笔记&#xff0c;不仅写起来慢&#xff0c;而且整理和分享起来也很麻烦。幸好&#xff0c;随着科技的发展&#xff0c;出现了一些能将说话的声音直…

【计算机网络】什么是socket编程?以及相关接口详解

&#x1f490; &#x1f338; &#x1f337; &#x1f340; &#x1f339; &#x1f33b; &#x1f33a; &#x1f341; &#x1f343; &#x1f342; &#x1f33f; &#x1f344;&#x1f35d; &#x1f35b; &#x1f364; &#x1f4c3;个人主页 &#xff1a;阿然成长日记 …

letcode 分类练习 x个数之和问题 15. 三数之和 18. 四数之和 454. 四数相加 II 383. 赎金信

letcode 分类练习 x个数之和问题 15. 三数之和 18. 四数之和 454. 四数相加 II 383. 赎金信 三数之和四数之和454. 四数相加 II383. 赎金信 三数之和 三数之和&#xff0c;双指针模版代码&#xff0c;注意去重逻辑&#xff0c;还有只需要去重第一和第二重循环&#xff0c;第三…

从Python翻译Go代码谈起:AI辅助编程的现状与展望

最近&#xff0c;一位同学使用GPT-4o将一个约300行的Python程序转换成Golang&#xff0c;正确率达到了90%。这引发了一个有趣的讨论&#xff1a;如果是整个项目规模的代码转换&#xff0c;准确率会如何&#xff1f;作为被的对象&#xff0c;我决定深入探讨这个话题&#xff0c;…