当前位置: 首页 > news >正文

[前端算法]动态规划

基于动态规划的思想来做题,我们首先要想到的思维工具就是“倒着分析问题”。“倒着分析问题”分两步走:

  1. 定位到问题的终点
  2. 站在终点这个视角,思考后退的可能性

爬楼梯

递归+记忆化搜索

自顶向下

var climbStairs = function(n) {let map=[]function dfs(n){if(n<=2) return nif(map[n] == undefined) map[n]=dfs(n-1)+dfs(n-2)return map[n]}return dfs(n)
};

记忆化搜索可以理解为优化过后的递归。递归往往可以基于树形思维模型来做,
我们基于树形思维模型来解题时,实际上是站在了一个比较大的未知数量级(也就是最终的那个n),来不断进行拆分,最终拆回较小的已知数量级(f(1)f(2))。这个过程是一个明显的自顶向下的过程。
在这里插入图片描述

动态规划则恰恰相反,是一个自底向上的过程。它要求我们站在已知的角度,通过定位已知未知之间的关系,一步一步向前推导,进而求解出未知的值。
在这道题中,已知 f(1)f(2) 的值,要求解未知的 f(n),我们唯一的抓手就是这个等价关系:

动态规划

自底向上

var climbStairs = function(n) {let dp=[]dp[1]=1;dp[2]=2;for(let i=3;i<=n;i++){dp[i] = dp[i-1]+dp[i-2]}return dp[n]
};

动态规划常见的题型

最优子结构,它指的是问题的最优解包含着子问题的最优解——不管前面的决策如何,此后的状态必须是基于当前状态(由上次决策产生)的最优决策。就这道题来说,f(n)f(n-1)f(n-2)之间的关系印证了这一点(这玩意儿叫状态转移方程,大家记一下)。

重叠子问题,它指的是在递归的过程中,出现了反复计算的情况。就这道题来说,图上标红的一系列重复计算的结点印证了这一点。
因此,这道题适合用动态规划来做。

对于动态规划,建议大家优先选择这样的分析路径:

  1. 递归思想明确树形思维模型:找到问题终点,思考倒退的姿势,往往可以帮助你更快速地明确状态间的关系
  2. 结合记忆化搜索,明确状态转移方程
  3. 递归代码转化为迭代表达(这一步不一定是必要的,1、2本身为思维路径,而并非代码实现。若你成长为熟手,2中分析出来的状态转移方程可以直接往循环里塞,根本不需要转换)。

最值问题

站在 amount 这个组合结果上的“后退”—— 我们可以假装此时手里已经有了 36 美分,只是不清楚硬币的个数,把“如何凑到36”的问题转化为“如何从36减到0”的问题

f(36) = Math.min(f(36-c1)+1,f(36-c2)+1,f(36-c3)+1......f(36-cn)+1)
function coinChange(coins, amount) {//dp[i] 代表总额为i的硬币最小个数let dp = [0];for (let i = 1; i <= amount; i++) {dp[i] = Infinity;for (let j = 0; j < coins.length; j++) {if (i >= coins[j]) {dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1);}}}if (dp[amount] === Infinity) {return -1;}return dp[amount];}

01背包问题

“倒推”法明确状态间关系

现在,假设背包已满,容量已经达到了 c。站在c这个容量终点往后退,考虑从中取出一样物品,那么可能被取出的物品就有 i 种可能性。我们现在尝试表达“取出一件”这个动作对应的变化,我用 f(i, c) 来表示前 i 件物品恰好装入容量为 c 的背包中所能获得的最大价值。现在假设我试图取出的物品是 i,那么只有两种可能:

  1. i 件物品在背包里
  2. i 件物品不在背包里
dp[i][v] = Math.max(dp[i-1][v], dp[i-1][v-w[i]] + c[i])

从变量入手:变量是 iv,但本质上来说 v 其实也是随着 i 的变化而变化的,因此我们可以在外层遍历i、在内层遍历 v。明白了这一点,我们就可以编码如下:

for(let i=1;i<=n;i++) {for(let v=w[i]; v<=c;v++) {dp[i][v] = Math.max(dp[i-1][v], dp[i-1][v-w[i]]+value[i])}
}
function knapsack(n, c, w, value) {//dp[i][j]表示前i个物品,背包容量为j时的最大价值//dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+value[i])let dp = [];//初始化for (let i = 0; i <= n; i++) {dp[i] = [];for (let j = 0; j <= c; j++) {dp[i][j] = 0;}}for (let i = 1; i <= n; i++) {for (let j = 1; j <= c; j++) {if (j >= w[i]) {dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - w[i]] + value[i]);} else {dp[i][j] = dp[i - 1][j];}}}return dp[n][c];}

最长上升子序列问题

300. 最长递增子序列 - 力扣(LeetCode)

子序列,在原有序列的基础上,删除0个或多个数,其他数的顺序保持不变得到的结果
f(i)来表示前i个元素中最长上升子序列的长度。若想基于 f(i) 求解出 f(i+1),我们需要关注到的是第 i+1 个元素和前 i 个元素范围内的最长上升子序列的关系,它们之间的关系有两种可能:

  1. 若第i+1个元素比前i个元素中某一个元素要大,此时我们就可以在这个元素所在的上升子序列的末尾追加第i+1个元素(延长原有的子序列),得到一个新的上升子序列。
  2. 若第i+1个元素并不比前i个元素中所涵盖的最长上升子序列中的某一个元素大,则维持原状,子序列不延长
function lengthOfLIS(nums) {//dp[i]表示以nums[i]结尾的最长上升子序列的长度//初始化,每个索引位都存在一个以自己为结尾的子序列,长度为1let dp = new Array(nums.length).fill(1);let max = 1;for (let i = 1; i < nums.length; i++) {for (let j = 0; j < i; j++) {if (nums[i] > nums[j]) {dp[i] = Math.max(dp[i], dp[j] + 1);}}if (max < dp[i]) {max = dp[i];}}return max;}
http://www.dtcms.com/a/330460.html

相关文章:

  • 【保姆级教程】CentOS 7 部署 FastDFS 全流程(避坑指南)
  • 【Docker】安装kafka案例
  • 深入解析 Spring IOC 容器在 Web 环境中的启动机制
  • ActiveReports 19.1 Crack
  • 新手向:Python条件语句(if-elif-else)使用指南
  • 初识HTML
  • 云原生俱乐部-k8s知识点归纳(1)
  • AI 编程实践:用 Trae 快速开发 HTML 贪吃蛇游戏
  • 游戏行业DevOps实践:维塔士集团基于Atlassian工具与龙智服务构建全球化游戏开发协作平台
  • LLM 中 语音编码与文本embeding的本质区别
  • 网络流初步
  • 版本更新!FairGuard-Mac加固工具已上线!
  • 【Unity3D实例-功能-移动】角色行走和奔跑的相互切换
  • Unity2022 + URP + Highlight plus V21配置和使用
  • Linux下使用Samba 客户端访问 Samba 服务器的配置(Ubuntu Debian)
  • 一颗TTS语音芯片给产品增加智能语音播报能力
  • 【无标题】卷轴屏手机前瞻:三星/京东方柔性屏耐久性测试进展
  • python自学笔记8 二维和三维可视化
  • 【深度学习】深度学习基础概念与初识PyTorch
  • 【C#补全计划】泛型约束
  • 从0开始的中后台管理系统-7(订单列表功能实现,调用百度地图打点以及轨迹图动态展示)
  • 数据结构--------堆
  • 18.14 全量微调实战手册:7大核心配置提升工业级模型训练效率
  • 阿里云RDS SQL Server实例之间数据库迁移方案
  • 通信算法之313:FPGA中实现滑动相关消耗DSP资源及7045/7035的乘法器资源
  • 工具栏扩展应用接入说明
  • React和Vue
  • Webpack Plugin 深度解析:从原理到实战开发指南
  • 使用AI编程自动实现自动化操作
  • Java 设计模式-组合模式