算法通关村 | 透彻理解动态规划

news2024/11/17 21:39:30

1. 斐波那契数列

1,1,2,3,5,8,13,..... f(n) = f(n-1) + f(n-2)

代码实现

    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=20时,count是21891次,当n=30的时候结果是2692537,接近270万,为什么这么高,因为里面存在大量的循环,下面图中,n=8时,f(6)就重复计算两次,很多结点会被重复计算。

如何将其优化减少重复计算,这也是下面推导动态规划需要考虑的,可以将计算结果保存到数组中,f(n)的值保存在数组中,f(n)=arr[n],某个位置已经被计算出来,下次需要的时候直接取出来,不需要再递归。

    public static int[] arr = new int[50];
    public static int count_3 = 0;

    public static void main(String[] args) {
        Arrays.fill(arr,-1);
        arr[0] = 1;
        System.out.println(fibonacci(31));
    }
    static 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];
        }
    }

2 路径连环炮

文中的动态规划我们简称DP,路径相关的问题来分析动态规划,路径问题易于画图,方便理解,循序渐进的理解DP

2.1 第一炮:基本问题统计路径总数

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

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

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

第一炮,我们研究如何通过递归来解决此问题,如下图所示,从起点开始要么向右,要么向下,每一种否会导致剩下的区间减少了一列或一行,形成两个不同区间的过程,每个区间继续以红点为起点继续上述操作,所以这就是一个递归的过程,

从图中寻找规律,目标从起点到终点,只能向右或向下,

1. 向右走一步,起点下面灰色的不会再被访问,后面剩一个3X1的矩阵,只能一直往下走,只有一种路径,

2. 向下走一步,起点右侧不不能再被访问,剩一个2X2的矩阵,还剩两种路径,

这是3X2的矩阵,一共有3中路径,一个2X2的矩阵和3X1的矩阵路径之和。

同样我们推导一个3X3的矩阵,就是一个3X2的矩阵和2X3的路径之和,

所以,对于一个mxn的矩阵,求路径的方法是serch(m,n)=search(m-1,n)+search(m,n-1);

    public int uniquePath(int m,int n){
        return search(m,n);
    }

    private int search(int m, int n) {
        if (m == 1 || n == 1){
            return 1;
        }
        return search(m-1,n) + search(m,n-1);
    }

对于3X3的矩阵,我们可以用二叉树表示

我们可以发现,和文章开头我们提到的一样,中间有重复计算,1,1计算了两次,那么如何优化

2.2 第二炮:使用二维数组优化递归

我们知道上面出现了重复计算,{1,1}出现两次,在{1,1}位置处,不管是从{0,1}还是{1,0}到来,都会产生两种走法,我们可以用二维数组记忆化搜索就不用两次遍历了,

每个格子都表示从起点开始到当前的位置有几种方式,这样我们通过计算路径的时候可以先查下二维数组有没有记录,有就直接读,没有再计算,这样就可以避免大量的重复计算,这就是记忆化搜索。

从上面的分析我们得到两个规律:

1.第一行和第一列都是1.

2.其他格子的值都是其左侧和上方格子之和,对于mXn的格子都适应。

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

2.3 第三炮:滚动数组:用一维代替二维数组

第三炮,我们通过滚动数组来优化此问题,上面缓存空间使用二维数组,占据空间大,能否进一步优化,我们看上面的二维数组找出规律。

发现除了第一行和第一列都是1外,每个位置都是其左侧和上方的格子之和,可以用一个大小为n的一维数组来解决:

将几个一维数组拼接起来,就是和上面的二维数组完全一样的,反复更新数组的策略就是滚动数组,计算公式是:dp[j] = dp[j]+dp[j-1]

    public int uniquePaths(int m, int n){
        int[] dp = new int[n];
        //将数组初始元素初始为1
        Arrays.fill(dp,1);
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                //等式左边的dp[j]是上一次计算后的,再加上左边的dp[j-1]即为当前结果
                dp[j] = dp[j] + dp[j - 1];
            }
        }
        return dp[n -1];
    }

这个题目包含了DP(动态规划)里的多个方面,比如重复子问题、记忆化搜索、滚动数组等等,这就是最简单的动态规划了,只不过我们这里的规划是dp[j] = dp[j] + dp[j -1] ;不用进行复杂的比较和计算。

3. 理解动态规划

DP一般是让我们找最值的,例如最长公共子序列,最关键的是DP问题的子问题不是相互独立的,如果递归直接分解会导致重复计算指数级增长,开头的热身题,而DP的最大价值是为了消除冗余,加速计算。

动态规划解决什么问题:A求有多少种走法,B输出所有的走法

动态规划计算效率高,但是不能找到满足要求的路径,

区分动态规划和递归最重要的一条是:动态规划只关心当前结果是什么,怎么来的就不管了,所以动态规划无法获得完整的路径,这与回溯不一样,回溯能够获得一条甚至所有满足要求的完整路径。

DP的基本思想是将待求解问题分解成若干个子问题,先求子问题,再从这些子问题中得到原问题的解,既然要找最值,那么必然要做的就是穷举来找所有的可能,然后选择“最”的那个,这就是为什么再DP代码中大量判断逻辑都会被套上min()或者max(),

既然穷举,那为什么还有DP的概念?这是因为穷举的过程中存在大量的重复计算,效率低下,所以我们要使用记忆化搜索等方式来消除不必要的计算,所谓记忆化搜索就是将已经计算好的结果先存在数组里面,后面就直接读就不再重复计算了,

既然记忆化能解决问题,为啥DP这么难,因为DP问题一定具备“最优子结构”,这样才能让记忆时得到准确结果,至于什么时最优子结构,后面还有具体问题,

有了最优子结构,还要正确的“状态转移方程”,才能正确的穷举,也就是递归关系,大部分递归都可以通过数组实现,因此代码结构一般是这样的for循环,这就是DP的基本模板:

//初始化base case ,也就是刚开始的几种场景,有几种枚举
        dp[0][0][...] = base case
//进行状态转移
        for 状态1 状态1的所有取值
                for 状态2 in 状态2的所有取值
                        dp[状态1][状态2] = 求最值Max(选择1,选择2,...)

动态规划题目有三种基本类型:

1. 计数有关,例如求多少种方式到右下角,有多少种方式选出K个数是使得什么什么的问题,不关心路径是什么

2. 求最大值最小值,最多最少等,例如最大数字和,最长上升子序列,最长公共子序列,最长回文序列等

3. 求存在性,例如取石子游戏,先手是否必胜,能不能选出k个数使得什么什么等等

不管那种解决问题的模板也是类似的

  • 第一步:确定状态和子问题,也就是枚举出某个位置所有的可能性,对于DP,大部分题目分析最后一步更容易一些,得到递推关系,同时将问题转换为子问题。
  • 第二步:确定状态转移方程,也就是数组要存储什么内容,很多时候状态确定之后,状态转移方程也就确定了,前两步也可作为为第一步骤
  • 第三步:确定初始和边界条件情况,注意细心,尽量周全
  • 第四步:按照从小到大的顺序计算:f[0],f[1],f[2]

我们自始至终,都要在大脑里装一个数组(可能是一维,也可能是二维),要看这个数组每个元素的含义是什么(也就是状态),要看每个数组是根据谁来算的(状态转移方程),然后就是从小到大挨着将数组填满(从小到大计算,实现记忆化搜索),最后看那个位置是我们想要的结果。

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

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

相关文章

探索单链表数据结构:理解与实现

文章目录 &#x1f34b;引言&#x1f34b;什么是单链表&#xff1f;&#x1f34b;单链表的基本操作&#x1f34b;单链表的实现&#x1f34b;练习题&#x1f34b;总结 &#x1f34b;引言 在计算机科学和数据结构中&#xff0c;链表是一种基本且重要的数据结构&#xff0c;用于存…

基于springboot财务管理系统springboot006

大家好✌&#xff01;我是CZ淡陌。一名专注以理论为基础实战为主的技术博主&#xff0c;将再这里为大家分享优质的实战项目&#xff0c;本人在Java毕业设计领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目&#xff0c;希望你能有所收获&#xff0c;少走一些弯路…

【zabbix监控三】zabbix之部署代理服务器

一、部署代理服务器 分布式监控的作用 分担server的几种式压力解决多机房之间的网络延时问题 1、搭建proxy主机 192.168.88.50 关闭防火墙和安全机制&#xff0c;修改主机名 设置 zabbix 的下载源&#xff0c;按照 zabbix-proxy rpm -ivh \ https://mirrors.aliyun.com/zab…

01-Maven入门

1 Maven简介 1.1 Maven是什么 Maven 是一个用于构建和管理 Java 项目的工具。它提供了一种标准化的项目结构和构建流程&#xff0c;可以自动化地处理项目的依赖管理、编译、测试、打包和部署等任务。 Maven 使用一个基于 XML 的配置文件&#xff08;pom.xml&#xff09;来描…

【C++面向对象侯捷】12.虚函数与多态 | 13.委托相关设计【设计模式 经典做法,类与类之间关联起来,太妙了,不断的想,不断的写代码】

文章目录 12.虚函数与多态举例&#xff1a;委托 继承【观察者模式】13.委托相关设计Composite 组合模式Prototype 原型模式 12.虚函数与多态 纯虚函数 一定要 子类重新定义的 继承和复合 关系下的构造和析构 举例&#xff1a;委托 继承【观察者模式】 13.委托相关设计 问题…

建材行业微信小程序开发实战经验分享

随着互联网的迅猛发展&#xff0c;电子商务成为了人们日常购物的主要方式之一。而微信小程序的兴起更是为商家提供了一个全新的线上销售渠道。在建筑材料选购领域&#xff0c;开发一个微信小程序商城平台能够有效地提升用户的购物体验和商家的销售效益。 为了实现建筑材料选购平…

Android studio中如何下载sdk

打开 file -> settings 这个页面, 在要下载的 SDK 前面勾上, 然后点 apply 在 platforms 中就可以看到下载好的 SDK: Android SDK目录结构详细介绍可以参考这篇文章: 51CTO博客- Android SDK目录结构

OpenAI ChatGPT API 文档之 Embedding

译者注&#xff1a; Embedding 直接翻译为嵌入似乎不太恰当&#xff0c;于是问了一下 ChatGPT&#xff0c;它的回复如下&#xff1a; 在自然语言处理和机器学习领域&#xff0c;"embeddings" 是指将单词、短语或文本转换成连续向量空间的过程。这个向量空间通常被称…

Python程序设计实例 |爬取网络中的小说

网络文学是新世纪我国流行文化中的重要领域&#xff0c;年轻人对网络小说更是有着广泛的喜爱。本文以抓取网络小说正文为例编写一个简单、实用的爬虫脚本。 01、分析网页 很多人在阅读网络小说时都喜欢本地阅读&#xff0c;换句话说就是把小说下载到手机或者其他移动设备上阅读…

TQ210-Bootloader-Uboot(LTS)

Bootloader的作用 Bootloader是位于计算机系统启动过程中的程序&#xff0c;它的主要作用是将操作系统从磁盘等外部存储介质加载到计算机内存中&#xff0c;并启动操作系统执行。Bootloader通常包括硬件初始化、自检、异常处理和启动操作系统等功能。它是计算机系统中非常重要…

Mac电脑系统怎么样才能干干净净地卸载应用程序?

Mac系统怎么样才能干干净净地卸载应用程序&#xff0c;不留下隐私数据和用户信息呢&#xff1f;如果有方法的话&#xff0c;那么该方法对于Mac电脑小白是否友好呢&#xff1f; CleanMyMac就是一款用于清理Mac系统下应用程序的一款清理工具&#xff0c;其内置了应用程序的安全卸…

第二证券:智能网联汽车产业迎催化 容量电价政策出台可期

昨日&#xff0c;A股延续调整态势&#xff0c;沪指失守3100点&#xff0c;深成指跌破10000点大关&#xff0c;创业板跌约1%再创阶段新低&#xff1b;两市成交额保持在地量水平&#xff0c;再创年内新低。到收盘&#xff0c;沪指跌0.77%报3084.7点&#xff0c;深成指跌0.9%报998…

速卖通新品如何推广,速卖通的推广渠道有哪些?——站斧浏览器

速卖通的推广渠道非常多样化&#xff0c;卖家可以根据自己的需求和预算选择合适的渠道来推广产品&#xff0c;提高曝光度和销售量&#xff0c;能够有效地提高产品的知名度和信任度。 速卖通新品如何推广&#xff1f; 速卖通上有数以百万计的卖家&#xff0c;每天都有大量的新…

批量寄件教程

快递行业的发展&#xff0c;和企业之间其实是正向的影响。为什么这么说呢&#xff1f;企业因公寄件&#xff0c;能为快递公司贡献一定寄件量&#xff0c;而快递行业的发展&#xff0c;不管是运输速度的提升&#xff0c;服务质量的提高&#xff0c;都能为企业的发展提供帮助&…

气膜建筑在施工工期方面的优势

充气膜建筑基础处理简单&#xff0c;迁移的损耗非常小&#xff0c;拆装方便&#xff0c;可快速安装&#xff0c;快速拆卸&#xff0c;可以很容易地建成季节性建筑&#xff0c;解决露天场馆因为“雨、晒、冷、雪”等导致部分时间不能营业的难题。 气膜建筑的柔性特点及其简约性使…

Stable Diffusion WebUI插件posex安装以及无法使用完美解决办法汇总

posex是一个很好用的3Dopenpose编辑器。 我们只需要去官网找到源码就可以查看其用法。 对于安装大家应该都知道怎么去安装。 1. 如何安装 (1)一体包安装方式 类似于秋叶一体包直接在webui界面搜索posex就可以直接install。 最新版本好像已经取消了。 (2)手动安装方式…

记一次MySQL安装过程中遇到的问题

由于太久没用MySQL&#xff0c;今天在重装MySQL时遇到一个问题&#xff0c;被卡了近2个小时。。。。。。 由于我本人原先安装过MySQL&#xff0c;所以在重装的时候必须要先卸载原先的MySQL。 下面先给出正确的卸载流程&#xff08;作者就是在卸载的时候操作失误导致安装过程被…

Python灰帽编程——定制EXP之RCE

文章目录 定制EXP之RCE1. 常见模块介绍1.1 base641.1.1 base64 编码1.1.2 base64 解码 1.2 string 2. 常规 EXP 编写2.1 phpstudy_2016-2018_rce2.1.1 漏洞利用脚本2.1.2 进阶脚本 2.2 SQL 注入 EXP2.2.1 布尔盲注2.2.2 延时注入 2.3 metinfo_5.0.4 EXP编写2.3.1 SQL注入漏洞 3…

【Linux操作系统教程】用户管理与权限管理你真的懂了吗(三)

&#x1f604;作者简介&#xff1a; 小曾同学.com,一个致力于测试开发的博主⛽️&#xff0c;主要职责&#xff1a;测试开发、CI/CD 如果文章知识点有错误的地方&#xff0c;还请大家指正&#xff0c;让我们一起学习&#xff0c;一起进步。&#x1f60a; 座右铭&#xff1a;不想…

早餐与风景

来吧&#xff0c;我用流水账描述下这一天。 时维九月&#xff0c;北京的早上有点冷&#xff0c;因为今天有个市场活动要去支撑&#xff0c;按照会议时间的要求&#xff0c;我需要在早上7点半就赶到会场&#xff0c;所以昨天晚上我加班到凌晨处理完了今天要给出去的材料&#xf…