java算法day27

java算法day27

  • 动态规划初步总结
  • 509 斐波那契数
  • 杨辉三角
  • 打家劫舍
  • 完全平方数

动态规划初步总结

如果你感觉某个问题有很多重叠子问题,使用动态规划是最有效的。
动态规划的过程就是每一个状态一定是由上一个状态推导出来的,这一点就区分于贪心了。贪心是“直接”从局部选最优的。

看到目前的各种教程,目前能看懂的动态规划思想总结就是:
这三步:
1、穷举法(暴力搜索)
2、记忆化搜索(剪枝)
3、改写成迭代形式。

目前我只能一步步的做题来体会动态规划。


509 斐波那契数列

目前就拿着上面总结的思想来做做题。

拿到这个题,我上来就想到了递归解法(这也对应着上面的第一点)。然后快速就交了,过了,但是超越百分之5。足以见得效率非常的低。

class Solution {public int fib(int n) {if(n == 0){return 0;}if(n == 1){return 1;}return fib(n-2) + fib(n-1);}
}

问题出在哪里,从递归树里就清楚了
在这里插入图片描述
从这个计算过程可以看到,存在着大量的重复计算。比如计算f(18)和f(19)的时候,都会计算f(17),而计算f(17)的过程又需要往下递归,意思说f(17)要算两次。往下那肯定存在更多的重复性计算。所以说这是需要优化的点。

如何优化,如何实现快速访问,这里我们可以想到哈希,下次我要计算之前,我先不着急递归,我先去hash里面看看有没有现成的,直接哪来用。那么按照这样的想法我们就可以创建一个哈希表,至于这个表是什么样的,可以根据题目来。

有时候可能还想不通,那这个hash里面的值是怎么记录下来的,那我只能说要把递归的过程想清楚。
通过上面的代码,算f(5) return f(4) + f(3) 。那么按程序执行顺序来说,肯定是先进f(4),然后一直往下走
比如f(4) return f(3) + f(2)
f(3) = f(2) + f(1)
然后f(2) = f(1)+f(0) 。可以见得,在不断下去的过程中,f(4),f(3),f(2),f(1),f(0)都会计算。所以说在这最左边的分支,其实就已经把别的分支那些重复的点全计算完了。所以hash就是在这里就完成了计算,怎么算?这个过程中程序要进行计算,hash表直接把这个结果也存到自己的哈希表里不就完事了。

从这个过程,再对上面的递归树做一个优化,你可以看到这样的变化。
在这里插入图片描述
是不是感觉2^n级别的复杂度立马变o(n)了。
在这里插入图片描述
带备忘录的写法如下:

class Solution {public int fib(int n) {if(n<1){return 0;}int[] memo = new int[31];return helper(n,memo);}int helper(int n,int[] memo){if(n == 1 || n == 2){return 1;}if(memo[n]!=0){return memo[n];}memo[n] = helper(n-1,memo)+helper(n-2,memo);return memo[n];}}

这么这就是dp了吗?还不是!
这其实还是一个自顶向下的解法。

真正的dp是什么。真正的dp是自底向上,从最小的子问题开始,然后逐步构造最大的解,直到达到目标。

动态规划的本质:动态规划的核心是通过解决子问题来解决更大的问题,在斐波那契数的例子中,我们直到每个数都是前两个数的和。

所以这里可以总结出动态规划问题的真正含义了。
我先来说说动态规划是如何解决这个问题的,首先如果清楚子问题是什么样的,然后清楚子问题的初状态,那么我就能够直接从这个子问题出发,不断地进行状态运算,直到状态转移到最终结果。然后这个得到下一个状态的方法,就是所谓的状态转移方程。

所以这里可以总结一个结论:
也就是很多教程里面教的方法:
子问题的识别
正如你所说,清楚地识别子问题是关键。在斐波那契数列中,子问题就是计算每个 F(i)。

初始状态(基本情况)
这些是已知的最小子问题的解。在斐波那契数列中,F(0) = 0 和 F(1) = 1。

状态转移
这是从一个子问题到另一个子问题的过程。在斐波那契数列中,状态转移方程是 F(i) = F(i-1) + F(i-2)。

最终状态
这是我们最终要求解的问题。在斐波那契数列中,就是计算 F(n)。

从初始状态到最终状态
正如你所说,我们可以从初始状态开始,通过不断应用状态转移方程,最终达到我们想要的结果。

说的更直白一点:
也就是说,如果我在做题的过程中,搞清楚了什么是子问题,还有初始状态,知道下一个状态该如何正确计算。那么我就能从这个初始状态直接往后推,直到推出最后的正确答案。


然后就立马写出了这个题。感觉还是非常清楚的。
子问题怎么找? 这里我看网上一般是通过推广的办法,由f(n)来思考那么是要计算f(n-1)+f(n-2)。那么推广到i就是dp[i] = dp[i-1]+dp[i-2];所以子问题就是计算dp[i-1]和dp[i-2]。往最初的状态倒就是从dp[0] 和 dp[1]开始
状态转移方程是啥? 就是怎么计算下一个状态,这里状态转移方程就是dp[i] = dp[i-1]+dp[i-2]
最终状态怎么确定? 就是看dp要到哪停下来。这里就是算dp[n]

dp数组到底怎么初始化?这个我认为一是要根据问题,而是要根据数据的边界。即最大可能取到的状态来确定长度。

class Solution {public int fib(int n) {//快速特判if(n<2){return n;}int[] dp = new int[n+1];dp[0] = 0;dp[1] = 1;for(int i = 2;i<=n;i++){dp[i] = dp[i-1]+dp[i-2];}return dp[n];}
}

提交之后会发现,效率和备忘录那个写法的效率相同
因为这俩的计算纯粹是反过来的关系

然后还可以再进一步优化

是不是发现我们其实只要这个最后的dp[n],甚至感觉这个dp数组有点多余了。
确实是,我们其实就只需要不断的迭代更新后面两个数字 即 dp[i-1] 和 dp[i-2]
这个技巧就是所谓的状态压缩 空间复杂度直接优化为o1了。
怎么理解状态压缩的应用?就是想想原来的dp数组,有没有一些空间是不必要存储的。

所以这里还有一个究极版本。

class Solution {public int fib(int n) {if(n<2){return n;}int pre = 0;int cur = 1;for(int i = 2;i<=n;i++){//sum是下一个状态int sum = pre+cur;pre = cur;cur = sum;}return cur;}
}

零钱兑换

上面的斐波那契还体现不出dp。因为没有求最值问题,现在来看看这个题。
我第一想法就是一个大回溯直接爆搜。果不其然第32个例子直接超时。

接下来就只能看看题解了,用dp来做这个题。

这里得到第一个知识点:能用dp,首先要复合最优子结构,子问题间必须互相独立。
啥叫互相独立,就是子问题之间没有互相影响。
简单来说就是考试,你要拿最高分,比如一共两名,语文和数学。
要拿最高分就是数学考最高,语文考最高。这样就叫独立。
如果你数学考得高了,会导致你语文考不高,那就不叫独立。

所以要自己看看子问题之间有没有互相制约关系,是否相互独立

还有这个看待子问题的角度也非常重要。有时候你子问题找错了,那就可能做不出来了。
对于本题而言,子问题是这样拆分的,比如追求amont = 11(原问题),如果你知道凑出amont = 10的最少硬币数(子问题),那么只需要把子问题的答案(再选一枚面值为1的硬币),就是原问题的答案。还有这怎么看出子问题之间没有互相限制,因为硬币数量是没有限制的。


这里又纠正了我看待动态规划问题的思路了。一开始我是没理解倒这个问题中的独立。
1、动态规划的思考方向:
首先,动态理解是要自底向上思考的,而不是从amount=11开始往下想,我从大的开始想,那么我就会老是去想我最后一个面值为1了,那不是有可能对我前面的问题产生影响?
这里主要是方向想错了
2、子问题的定义:
对于金额i,子问题是凑出金额i所需的最少硬币数
3、独立性本质:
当我们说子问题是独立的,我们指的是,求解金额i的最优解,不依赖于如何求解i+1,i+2等更大的金额。(说白了还是方向看错了)。
4、为什么看起来不独立(我之前的思想):
因为我方向想反了。

现在来模拟这个问题
举例说明:
假设硬币面值为 [1, 2, 5],我们要凑出 11。

我们首先解决小额问题:1, 2, 3, 4, 5, …
当我们到达 11 时,我们考虑的是:
dp[11] = min(dp[10] + 1, dp[9] + 1, dp[6] + 1)
这里的 dp[10], dp[9], dp[6] 都已经是各自最优的解了
我们不是在考虑 “用1个1硬币然后解决10”,而是在比较 “10的最优解+1” 和其他可能性

独立性的证明:

如果 dp[10] 是最优的(假设是3个硬币),那么无论我们如何解决 11,都不会影响 10 的这个最优解。
即使我们选择了 “dp[9] + 1个2硬币” 作为 11 的最优解,这也不会改变 10 的最优解仍然是 3 个硬币这个事实。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mysql死锁排查

Mysql 查询是否存在锁表有多种方式&#xff0c;这里只介绍一种最常用的 1、查看正在进行中的事务 SELECT * FROM information_schema.INNODB_TRX 2、查看正在锁的事务 SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS; 3、查看等待锁的事务 SELECT * FROM INFORMATION_SCHEMA.I…

视频VIP收费会员播放帝国CMS模板HTML5自适应手机多种运营模式

采用帝国CMS最新版核心制作&#xff0c;自适应响应式手机平板浏览&#xff0c;手机浏览器非常舒服哦&#xff01;多种运营模式。用户中心逻辑和页面&#xff0c;都已经制作完整&#xff0c;可以搭建后稍微修改即可使用&#xff01; 模板特点&#xff1a; 支持多集和单集播放&…

Kafka动态授权认证:利用SASL/SCRAM机制提升安全性

摘要 Apache Kafka是一个流行的分布式流处理平台&#xff0c;其安全性对于保护数据传输至关重要。SASL/SCRAM&#xff08;Simple Authentication and Security Layer/Salted Challenge Response Authentication Mechanism&#xff09;是一种认证机制&#xff0c;可以为Kafka集…

从华为出走的工控龙头,豪横收购法国顶尖软件龙头~

导语 大家好&#xff0c;我是社长&#xff0c;老K。专注分享智能制造和智能仓储物流等内容。 新书《智能物流系统构成与技术实践》 近日&#xff0c;业界传来震撼消息&#xff0c;华为系企业汇川科技正式宣布&#xff0c;已完成对法国顶尖工业软件企业Irai的全资收购。 这一战略…

【LLM】-12-部署Langchain-Chatchat-0.3.x版本

目录 1、0.3与0.2的功能对比 2、0.3.x支持多种部署方式 2.3、源码安装 2.3.1、项目源码下载 2.3.2、创建conda环境 2.3.3、安装poetry 2.3.4、安装依赖库 2.3.5、项目初始化 2.3.6、初始化知识库 2.3.7、启动服务 2.3.8、配置说明 2.3.8.1、basic_settings.yaml 2…

一馆多用,四季皆宜:气膜体育馆的优势与应用—轻空间

促进城市体育发展 装配式气膜体育馆以其便捷的安装、灵活的使用和多功能性&#xff0c;迅速在全国范围内得到推广。这种体育场馆不仅适用于篮球、羽毛球、网球等传统室内运动&#xff0c;还能根据需要灵活改造成游泳馆、滑冰场等特殊场地。这种多功能性使得气膜体育馆在城市中得…

甄选范文“论数据分片技术及其应用”软考高级论文,系统架构设计师论文

论文真题 数据分片就是按照一定的规则,将数据集划分成相互独立、正交的数据子集,然后将数据子集分布到不同的节点上。通过设计合理的数据分片规则,可将系统中的数据分布在不同的物理数据库中,达到提升应用系统数据处理速度的目的。 请围绕“论数据分片技术及其应用”论题…

【ThingsBoard初体验】本地运行源码踩坑记录

前言 运行源码之前&#xff0c;请先编译源码。这很重要&#xff01;&#xff01;&#xff01; 官网源码编译教程&#xff1a;http://www.ithingsboard.com/docs/user-guide/contribution/yuanmabianyi/ 如果编译过程中出现报错&#xff0c;请看我上一篇文章&#xff1a;【Thing…

使用ssh-remote连接远程vscode运行yolo项目时的一点坑

使用ssh-remote连接远程vscode运行yolo项目时的一点坑 1.坑1 因为我是直接下载的release包&#xff0c;然后运行 pip install -e .来下载依赖的&#xff0c;那么这个时候需要使用YOLO时都需要在下载的release文件的目录下的py文件才能生效 比方说我下载的yolov8(ultralytic…

从功能出发:优化超市商品陈列,助力销售额提升

随着时代的发展&#xff0c;竞争的加剧&#xff0c;人们的生活节奏加快&#xff0c;时间观念越来越强。在这种情形下&#xff0c;作为超市&#xff0c;怎样为顾客提供一个舒适方便的购物环境&#xff0c;尽可能让顾客逛完整个卖场&#xff0c;满足一站式购足呢&#xff1f;除了…

[PM]面试题-工作问题

画一个原型需要多久?写一篇PRD文档需求多久? 时间长短取决于项目规模和业务难度, 规模大难度高,就要花费很长的时间, 规模下难度低时间就短, 一般来说, 1-2周的时间就可以完成原型和RED文档 市场需求文档写什么? 从打到下进行编写, 大的方面以市场为主体,包括市场规模, 发…

【中项】系统集成项目管理工程师-第9章 项目管理概论-9.1PMBOK的发展与9.2项目基本要素

前言&#xff1a;系统集成项目管理工程师专业&#xff0c;现分享一些教材知识点。觉得文章还不错的喜欢点赞收藏的同时帮忙点点关注。 软考同样是国家人社部和工信部组织的国家级考试&#xff0c;全称为“全国计算机与软件专业技术资格&#xff08;水平&#xff09;考试”&…

智能优化算法(三):遗传算法

文章目录 1.问题描述2.遗传算法2.1.算法概述2.2.编码操作2.3.选择操作2.4.交叉操作2.5.变异操作2.6.算法流程 3.算法实现3.1.MATLAB代码实现3.2.Python代码实现 4.参考文献 1.问题描述 \quad 在利用启发式算法求解问题时&#xff0c;我们常常需要应用遗传算法解决函数最值问题&…

计算计中的推理与调用

计算计是一个新的概念&#xff0c;它强调了计算与谋算&#xff08;算计&#xff09;的融合和互动过程。这个概念指的是在现代科技和商业环境中&#xff0c;越来越多地将计算能力与战略性思维结合起来&#xff0c;以解决复杂问题、优化决策和实现目标。具体来说&#xff0c;计算…

vue3组件通信(一)

组件通信 一.props(父<>子)二.自定义事件&#xff08;子>父&#xff09;三.mitt(实现任意组件通信)四.v-model(父<>子)(1).v-model的本质(2).组件标签中v-model的本质(3).$event到底是什么 概况 一.props(父<>子) 使用频率最高 若 父传子&#xff1a;属性…

红酒标签设计:艺术与品味的结合

在红酒的世界里&#xff0c;每一瓶酒都如同一位优雅的舞者&#xff0c;在酒柜的舞台上静静诉说着自己的故事。而红酒的标签&#xff0c;则是这位舞者身上较华丽的舞裙&#xff0c;它不仅是红酒的身份证明&#xff0c;更是艺术与品味的很好结合。今天&#xff0c;我们就来聊聊红…

【Vue3】组件生命周期

【Vue3】组件生命周期 背景简介开发环境开发步骤及源码 背景 随着年龄的增长&#xff0c;很多曾经烂熟于心的技术原理已被岁月摩擦得愈发模糊起来&#xff0c;技术出身的人总是很难放下一些执念&#xff0c;遂将这些知识整理成文&#xff0c;以纪念曾经努力学习奋斗的日子。本…

基于vue2 + Ant Design 封装input(输入)下拉Table表格

封装 AInputTable 组件 <!--下拉Table--> <template><div class"input-select-table" ref"inputTableRef" v-clickoutside"handleHide"><div class"input-select-table-input" click"disabled?this:hand…

【信创】samba的命令行使用 _ 统信 _ 麒麟 _ 中科方德

原文链接&#xff1a;【信创】samba的命令行使用 | 统信 | 麒麟 | 中科方德 Hello&#xff0c;大家好啊&#xff01;今天给大家带来一篇关于在信创终端操作系统上使用Samba命令操作的文章。Samba是一种用于实现文件和打印共享的免费软件&#xff0c;它允许不同操作系统&#xf…

Android studio IDE 使用日志 2024/7/30

Android studio IDE 使用日志 时间:2024/7/30 11:10 配置 安装中文语言包,汉化操作界面:下载地址 根据版本信息下载 设置中选择安装插件,选择压缩包自动安装 项目的文件夹目录结构 .gradle :包含了Gradle构建系统,自动编译工具产生的文件 .idea :包含IDEA&#xff08;‌A…