算法通关第十九关-青铜挑战理解动态规划

大家好我是苏麟 , 今天聊聊动态规划 .

动态规划是最热门、最重要的算法思想之一,在面试中大量出现,而且题目整体都偏难一些对于大部人来说,最大的问题是不知道动态规划到底是怎么回事。很多人看教程等,都被里面的状态子问题、状态转移方程等等劝退了。
其实,所谓的状态就是一个数组,动态规划里的状态转移方程就是更新这个数组的方法。这一关,我们先理解动态规划到底怎么回事。

大纲

    • 热身 : 斐波那契数列
    • 路径连环问题
      • 基本问题 : 统计路径总数
      • 用二维数组优化递归
      • 滚动数组 : 用一维数组代替二维数组
      • 拓展问题 : 最小路径和
    • 理解动态规划

热身 : 斐波那契数列

首先来感受一下什么是重复计算记忆化搜索

public class FibonacciTest {
    public static int count = 0;

    public static void main(String[] args) {
        fibonacci(20);
        System.out.println("count:" + count);
    }

    public static int fibonacci(int n) {
        System.out.println("斐波那契数列");
        count++;
        if (n == 0) {
            return 1;
        }
        if (n == 1 || n == 2)
            return n;
        else {
            return fibonacci(n - 1) + fibonacci(n - 2);
        }
    }
}

这个就是斐波那契数列,当n为20时,count是21891次。而当n=30 的时候结果是2692537,也就是接270万。如果纯粹只是算斐波那契数列,我们可以直接循环:

    public static int count_2 = 0;

    public int fibonacci(int n) {
        if (n <= 2) {
            count_2++;
            return n;
        }
        int f1 = 1;
        int f2 = 2;
        int sum = 0;
        for (int i = 3; i <= n; i++) {
            count_2++;
            sum = f1 + f2;
            f1 = f2;
            f2 = sum;
        }
        return sum;
    }

n为30时也不过计算二十几个数的累加,但是为什么采用递归竟然高达270万呢?
因为里面存在大量的重复计算,数越大,重复越多。例如当n=10的时候,我们看下面的结构图就已经有很多重复计算了:
在这里插入图片描述

上面我们在计算f(10)时,可以看到f(9)、f(8)等等都需要计算,这就是重叠子问题。怎么对其优化一下呢?
可以看到这里主要的问题是很多数据都会频繁计算,如果将计算的结果保存到一个一维数组里。把 n 作为我们的数组下标,f(n)作为值,也就是 arr[n] = f(n)。执行的时候如果某人位置已经被计算出来了就更新对应位置的数组值,例如 f(4)算完了,就将其保存到arr[4]中,当后面再次要计算 f(4) 的时候,我们判断f(4)已经计算过,因此直接读取 f(4) 的值,不再递归计算。代码如下:

        public static int[] arr = new int[50];
        public static int count_3 = 0;
        Arrays.fill(arr, -1);
        arr[0] = 1;
        int fibonacci ( int n){
            if (n == 2 || n == 1) {
                count_3++;
                arr[n] = n;
                return n;
            }
            if (arr[n] != -1) {
                count_3++;
                return arr[n];
            } else {
                count_3++;
                arr[n] = fibonacci(n - 1) + fibonacci(n - 2);
                return arr[n];
            }
        }

在上面代码里,在执行递归之前先查数组看是否被计算过,如果重复计算了,就直接读取,这就叫”记忆化搜索“,就这么简单。

路径连环问题

基本问题 : 统计路径总数

描述 :

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。

问总共有多少条不同的路径?

题目 :

LeetCode 62. 不同路径 :

不同路径

在这里插入图片描述
分析 :

我们先从一个3x2的情况来分析:

在这里插入图片描述
我们的目标是从起点到终点,因为只能向右或者向下,从图中可以可以看到:

1.如果向右走,也就是图1的情况,后面是一个3x1的矩阵,此时起点下面的两个灰色位置就不会再访问了,只能从绿色位置一直向下走,只有一种路径。

2.如果是向下走,我们可以看到原始起点右侧的就不能再访问了,而剩下的又是一个2X2的矩阵,也就是从图中绿色位置到红色位置,此时仍然可以选择向右或者向下,一共有两种路径。

所以上面的情况加起来就是一共有3种。

我们再看一下3X3的 :
在这里插入图片描述
可以看到,一个3X3的矩阵下一步就变成了一个3X2或者2X3的矩阵,而总路径数,也是是两者各自的路径之和。
因此,对于一个mxn的矩阵,求路径的方法search(m,n)就是:search(m-1,n)+search(m,n-1);
递归的含义就是处理方法不变,但是问题的规模减少了

解析 :

注意 :递归的方式会超出时间限制

class Solution {
    public int uniquePaths(int m, int n) {
        return dp(m,n);
    }
    public int dp(int m,int n){
        if(n == 1 || m == 1){
            return 1;
        }
         return dp(m - 1,n) + dp(m,n - 1);
     }
} 

用二维数组优化递归

我们来优化递归的问题,研究如何结合二维数组来实现记忆化搜索.

从上面这个树也可以看到在递归的过程中存在重复计算的情况,例如1,1出现了两次,如果是一个NXN的空间,那 1.0 和 0,1 的后续计算也是一样的。从二维数组的角度,例如在位置(1,1)处,不管从(0,1)还是(1,0)到来,接下来都会产生2种走法,因此不必每次都重新遍历才得到结果。

在这里插入图片描述
为此,我们可以采取一个二维数组来进行记忆化搜索,算好的就记录在数组中,也就是这样子:
在这里插入图片描述
每个格子的数字表示从起点开始到达当前位置有几种方式,这样我们计算总路径的时候可以先查一下二维数组有没有记录,如果有记录就直接读,没有再计算,这样就可以大量避免重复计算,这就是记忆化搜索

根据上面的分析,我们可以得到两个规律:
1.第一行和第一列都是1。
2.其他格子的值是其左侧和上方格子之和。对于其他m,n的格子,该结论一样适用的,例如:
在这里插入图片描述
比如图中的4,是有上面的1和左侧的3计算而来,15是上侧的5和左侧的10计算而来。如果用公式表示就是:

在这里插入图片描述

解析 :

class Solution {
    public int uniquePaths(int m, int n) {
        int[][] arr = new int[m][n];
        arr[0][0] = 1;
        for(int i = 0;i < m;i++){
            for(int j = 0;j < n;j++){
                if(i > 0 && j > 0){
                    arr[i][j] = arr[i - 1][j] + arr[i][j - 1];
                }else if(i > 0){
                    arr[i][j] = arr[i - 1][j];
                }else if(j > 0){
                    arr[i][j] = arr[i][j - 1];
                }
            }
        }
        return arr[m - 1][n - 1];
    }
} 

滚动数组 : 用一维数组代替二维数组

我们通过滚动数组来优化此问题。上面的缓存空间使用的是二维数组,这个占空间太大了,能否
进一步优化呢?
我们再看一下上面的计算过程:
在这里插入图片描述
在上图中除了第一行和第一列都是1外,每个位置都是其左侧和上访的格子之和,那我可以用一个大小为n的一维数组解决来:

第一步,遍历数组,将一维数组所有元素赋值为1

在这里插入图片描述
第二步,再次从头遍历数组,除了第一个,后面每个位置是其原始值和前一个位置之和,也就是这样:
在这里插入图片描述
第三步:重复第二步:除了第一个,后面每个位置仍然是其原始值和前一个位置之和,也就是这样:

在这里插入图片描述

  • 继续循环,题目给的m是几就循环几次,要得到结果,输出最后一个位置的15就可以了.

上面这几个一维数组拼接起来,是不是发现和上面的二维数组完全一样的? 而这里我们使用了一个一维数组就解决了,这种反复更新数组的策略就是滚动数组.计算公式是:
在这里插入图片描述

解析 :

class Solution {
    public int uniquePaths(int m, int n) {
        int[] arr = new int[n];
        Arrays.fill(arr,1);
        for(int i = 1;i < m;i++){
            for(int j = 1;j < n;j++){
                arr[j] = arr[j - 1] + arr[j];
            }
        }
        return arr[n - 1];
    }
} 

拓展问题 : 最小路径和

描述 :

给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。

说明:每次只能向下或者向右移动一步。

题目 :

LeetCode 64. 最小路径和 :

最小路径和 :

在这里插入图片描述
分析 :

这道题是在上面题目的基础上,增加了路径成本概念。由于题目限定了我们只能[往下]或者[往右]移动,因此我们按照当前位置可由哪些位置转移过来 进行分析:

  • 当前位置只能通过[往下] 移动而来,即有f[i][j] = f[i-1][j] + grid[i][j]
  • 当前位置只能通过[往右]移动而来,即有 f[i][j] = f[i][j-1] + grid[i][j]
  • 当前位置既能通过[往下]也能[往右] 移动,即有f[i][j] = min(f[i][j - 1],f[i - 1][j]) + grid[i][j]

二维数组的更新过程,我们可以图示一下:

在这里插入图片描述
我们现在可以引入另外一个概念状态: 所谓状态就是下面表格更新到最后的二维数组,而通过前面格子计算后面格子的公式就叫状态转移方程。如果用数学表达就是:

在这里插入图片描述

所谓的确定状态转移方程就是要找递推关系,通常我们会从分析首尾两端的变化规律来入手。

解析 :

class Solution {
    public int minPathSum(int[][] grid) {
        int m = grid.length;
        int n = grid[0].length;
        int[][] arr = new int[m][n];
        for(int i = 0;i < m;i++){
            for(int j = 0;j < n;j++){
                if(i == 0 && j == 0){
                    arr[i][j] = grid[i];
                }else{
                    int top = i - 1 >= 0 ? arr[i - 1][j] + grid[i][j] : Integer.MAX_VALUE; 
                    int left = j - 1 >= 0 ? arr[i][j - 1] + grid[i][j] :
Integer.MAX_VALUE;
                    arr[i][j] = Math.min(top,left);
                }
            }
        }

        return arr[m - 1][n - 1];
    }
}

理解动态规划

DP能解决哪类问题? 直观上,DP一般是让找最值的,例如最长公共子序列等等,但是最关键的是DP问题的子问题不是相互独立的,如果递归分解直接分解会导致重复计算指数级增长。而DP最大的价值是为了消除冗余,加速计算 .

这期就到这里下期见 !

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Windows 10如何关闭系统自动更新(实用教程)

本章教程&#xff0c;用最简洁的方式介绍在windows10中如何关闭系统自动更新。 目录 一、关闭自动更新服务 二、关闭自动更新组策略 一、关闭自动更新服务 1、 winr 2、services.msc 3、找到并双击 Windows Update 修改启动类型为禁用 二、关闭自动更新组策略 1、winr 2、gp…

社交网络分析2(下):社交网络情感分析的方法、挑战与前沿技术

社交网络分析2&#xff08;下&#xff09;&#xff1a;社交网络情感分析的方法、挑战与前沿技术 写在最前面7. 词嵌入&#xff08;word embedding&#xff09;的主要目的是什么&#xff1f;结合某方法简要地说明如何实现词嵌入。主要目的实现方法示例&#xff1a;GloVe案例分析…

python深拷贝和浅拷贝

文章目录 浅拷贝深拷贝 刷完这60个标准库模块&#xff0c;成为Python骨灰级玩家 深拷贝和浅拷贝都是用于复制对象的概念。浅拷贝在复制对象时&#xff0c;仅复制其引用&#xff0c;而非复制对象本身。这意味着原对象和新对象都指向相同的内存地址&#xff0c;修改一个对象会影…

linux 文本信息查询grep;控制命令执行和管道操作符号

1、grep grep "keyword" /path/to/logfile获取查询结果最后一行 grep "runs/detect/train" test4.log | tail -n 12、linux控制命令执行和管道操作符号 &、|、; 和 &&、》、>、< ##例子&#xff1b;wandb disabled && yolo …

new一个对象

1.自己直接调用 function Person(name, age) {this.name name;this.age age;}let a1 new Person("小明", 20);let a2 new Person("小菜", 25);console.log(a1); 打印的对象: 2.自己模拟一个 function Person(name, age) {this.name name;this.age a…

当OneNote不同步时,你需要做些什么让其恢复在线

OneNote笔记本无法同步的原因有很多。由于OneNote使用OneDrive将笔记本存储在云中,因此可能会出现互联网连接问题,与多人联机处理笔记本时会出现延迟,以及从不同设备处理同一笔记本时会发生延迟。以下是OneNote不同步时的操作。 注意:本文中的说明适用于OneNote for Windo…

深度学习小白学习路线规划

作为深度学习的初学者&#xff0c;以下是一个建议的学习路线&#xff0c;可以帮助你逐步掌握图像分类、目标检测与跟踪、实例分割和姿态估计&#xff1a; 掌握这些&#xff0c;计算机视觉算是入门了&#xff01; 1. 基础知识&#xff1a; 学习Python编程语言&#xff0c;它是…

买手机,该买二手旗舰,还是买新款中端机?答案让人意外

如今的数码博主推荐手机的时候&#xff0c;都喜欢拿各种硬件参数来比较&#xff0c;而手机企业高管则喜欢在比不过硬件的时候谈体验&#xff0c;对于消费者来说&#xff0c;那么到底硬件参数重要&#xff0c;还是体验更重要&#xff1f; 笔者因为工作需要&#xff0c; 这两年买…

Idea maven打包时 报错 illegalArgumentException: Malformed \uxxxx encoding 解决方法

1 改变打包命令重新打包 在maven打包命令上加入 -e -X 2 找到报错类和方法 可以看到是 java.util.Properties#loadConvert类方法中有个throw new IllegalArgumentException( "Malformed \\uxxxx encoding.")&#xff0c;在此打断点 3 以Debug方式重新运行maven…

猿人学19题(原比赛平台)

这道题给我搞得有点懵了&#xff0c;我现在还没发现他到底要考察什么&#xff0c;这边我直接协商我的sessionid请求是直接就成功的。&#x1f602; 依旧是分析请求方式&#xff0c;抓包到返回数据的位置 现在可以知道这些数据是ajax返回的&#xff0c;请求的参数是page&#x…

Win7系统桌面出现白色透明框的解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

AntDesignBlazor示例——分页查询

本示例是AntDesign Blazor的入门示例&#xff0c;在学习的同时分享出来&#xff0c;以供新手参考。 示例代码仓库&#xff1a;https://gitee.com/known/BlazorDemo 1. 学习目标 分页查询框架天气数据分页功能表格自定义分页 2. 创建分页查询框架 Table组件分页默认为前端分…

腾讯云Linux云服务器禁Ping设置

腾讯云Linux服务器默认是允许ping包的&#xff0c;但是在一些情况下为了安全考虑起见&#xff0c;我们都会把服务器设置为禁ping的模式。 1、首先检查Linux服务器当前是否禁ping 执行命令&#xff1a; cat /proc/sys/net/ipv4/icmp_echo_ignore_all 备注&#xff1a; 0----代…

【️如何理解面向对象和面向过程】

✅如何理解面向对象和面向过程&#xff1f; 典型理解✅扩展知识仓✅面向对象的三大基本特征✅封装✅继承✅多态 ✅为什么Java不支持多继承&#xff1f;✅菱形继承问题✅Java 8 中的多继承 ✅面向对象的五大基本原则&#xff1f; 典型理解 面向过程把问题分解成一个一个步骤&…

文章解读与仿真程序复现思路——电网技术EI\CSCD\北大核心《耦合碳-绿证-消纳量市场的日前电量市场交易交互式优化》

这个标题描述了一种优化模型或算法&#xff0c;用于在日前电量市场中耦合碳排放权市场、可再生能源绿色证书市场和消纳量市场进行交易的交互式优化。我将解析标题的关键词和概念&#xff1a; 日前电量市场&#xff1a;指的是电力市场中进行短期调度和交易的市场&#xff0c;其…

ES6 面试题 | 11.精选 ES6 面试题

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

vue-element-admin如何把mock换成使用真实后台接口

1&#xff09;修改vue.config.js文件 use strict const path require(path) const defaultSettings require(./src/settings.js)function resolve(dir) {return path.join(__dirname, dir) }const name defaultSettings.title || vue Element Admin // page title// If you…

C++相关闲碎记录(15)

1、string字符串 #include <iostream> #include <string> using namespace std;int main (int argc, char** argv) {const string delims(" \t,.;");string line;// for every line read successfullywhile (getline(cin,line)) {string::size_type beg…

带你亲证AI应用开发的“奇点”时刻

带你亲证AI应用开发的“奇点”时刻 AI 应用开发——新的历史节点 事实上&#xff0c;没有任何一种突破能够不经历重重失败&#xff0c;不体验一轮轮的痛苦&#xff0c;就能直接展现在人类面前。AI 技术自诞生之初直至今日&#xff0c;其发展之路从未一帆风顺——辉煌与寒冬交…

SLAM算法与工程实践——相机篇:传统相机使用(1)

SLAM算法与工程实践系列文章 下面是SLAM算法与工程实践系列文章的总链接&#xff0c;本人发表这个系列的文章链接均收录于此 SLAM算法与工程实践系列文章链接 下面是专栏地址&#xff1a; SLAM算法与工程实践系列专栏 文章目录 SLAM算法与工程实践系列文章SLAM算法与工程实践…