js 实现动态规划

本文通过一个国王金矿挖掘的故事介绍了如何利用JavaScript实现动态规划算法,旨在优化时间和空间。首先分析了按性价比挖掘金矿的初始思路,然后提出通过动态规划自底向上求解问题,逐步计算出最佳的黄金挖掘方案,最后讨论了优化时间复杂度和空间占用的策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

js实现动态规划 , 优化时间 , 优化空间

详情见小灰的算法之旅 , 以下是我个人的见解
题目:
很久很久以前,有一位国王拥有5座金矿,每座金矿的黄金储量不同,
需要参与挖掘的工人人数也不同。例如有的金矿储量是500kg黄金,需 要5个工人来挖掘;有的金矿储量是200kg黄金,需要3个工人来挖 掘…… 如果参与挖矿的工人的总数是10。每座金矿要么全挖,要么不挖,不能 派出一半人挖取一半的金矿。要求用程序求出,要想得到尽可能多的黄金,应该选择挖取哪几座金矿?
在这里插入图片描述

一开始的思路:

首先我们想到的是 : 求出每个金矿的性价比 , 从高到低依次挖出金矿
挖矿的性价比 : 350/3 > 500/5 > 200/3=300/4=400/5
那么我们十个人

  • 先用 3 个人挖 350 的金矿 ,
  • 再用 5 个人去挖 500 的金矿 ,
  • 最后剩下 2 个人不能再挖矿
  • 此时我们的黄金数量是 850
这是否就是十个人能挖的最大黄金数量了呢 ? 答案不是

因为我们 用 5个人 去挖 500 的金矿 , 再用5个人去挖400的金矿 , 此时十个人刚好用完 , 并且黄金的数量是900

那么我们怎么才能获得黄金的最大数量呢 ? 这里就要引入动态规划来实现

动态规划就是把简单的问题转换成规模较小的子问题,再从简单的子问题自底向上一步一步递推,最终得到问题的最优解 , 这样说你可能还是云里雾里
其实我们算出做出每一个举动过后的结果 , 再和其它举动的结果做出比较 , 就可以计算出最好的结果 , 举个列子 :

p:[5,5,3,4,3], //金矿开采需要的工人数量
g:[500,400,350,300,200]//金矿储量

第一个500金矿我们可以挖也可以不挖

  • : 黄金数量+500 , 工人数量 - 5
  •  接下来我们就用剩下的工人去挖下一个金矿 , 此时我们已经获得了500黄金
    
  • 不挖 : 黄金数量 + 0 , 工人数量 - 0
  •  接下来我们就直接用不变的工人数量去挖下一个金矿
    

到第二个400金矿的时候

  • : 黄金数量+400 , 工人数量 - 5
  •    此时已经没有工人 ,  要加上第一次挖的500金矿 , 最终的黄金数量是900
    
  • 不挖 : 黄金数量 + 0 , 工人数量 - 0
  •   到现在为止我们的工人数量和黄金数量都没有变化
    

一直这样遍历下去 , 直到工人数量为 零 或者 可以挖的黄金数量为 零 , 返回我们挖出的黄金数量
在这里插入图片描述

由题可知 , 我们创建几个变量 :

w:工人的数量 ,n:金矿的数量 ,
gold:金矿和人数之间的关系(为了之后的思路比较清晰 , 我把这两个装到一个对象里面)

例如 : gold.g[0] 和 gold.p[0] 之间的关系就是 当金矿储量为 500 时需要 5 个人挖

w = 10 //工人的数量
n = 5  //金矿的数量 
let gold = {
   
   
    		p:[5,5,3,4,3],	//金矿开采需要的工人数量
    		g:[500,400,350,300,200]//金矿储量
}

一 . js实现动态规划

我们还要实现状态转移方程 :

  • 金矿数量或者工人数量为时 : F(n,w) = 0
  • 工人的数量不足以挖当前的金矿时 , 去挖下一个金矿 : F(n-1 , w)
  • 当前工人数量足以挖当前的金矿时 : (有两种选择)
    - 挖: F(w-gold.p[n-1],n-1,gold)+gold.g[n-1]
    - 不挖: F(w,n-1,gold)
  • 返回第三种情况的最大值 Math.max(F(w,n-1,gold),F(w-gold.p[n-1],n-1,gold)+gold.g[n-1])
let w = 10//工人数量
let n = 5 //可选金矿数量
let gold = {
   
   
    p:[5,5,3,
### 使用 JavaScript 实现动态规划算法 动态规划的核心思想是将问题分解为多个子问题,并通过递推的方式求解这些子问题的最优解,最终得到整个问题的最优解[^2]。以下是实现动态规划算法的主要步骤以及一个具体的例子。 #### 主要步骤 1. **定义状态**:明确 dp 数组或对象的意义,表示什么信息。 2. **初始化状态**:设置初始条件,例如边界情况。 3. **定义状态转移方程**:描述如何从前一步的结果计算下一步的结果。 4. **求解问题**:根据状态转移方程填充 dp 表格。 5. **输出结果**:从 dp 表格中提取所需答案。 --- #### 示例:斐波那契数列 斐波那契数列是一个经典的动态规划问题,其中第 n 项等于前两项之和。下面展示了一个使用动态规划的思想来计算斐波那契数列的 JavaScript 实现[^4]。 ```javascript function fibonacci(n) { if (n <= 0) return 0; if (n === 1) return 1; const dp = [0, 1]; // 初始化状态 for (let i = 2; i <= n; i++) { dp[i] = dp[i - 1] + dp[i - 2]; // 定义状态转移方程 } return dp[n]; // 返回结果 } // 测试用例 console.log(fibonacci(10)); // 输出 55 ``` 在这个例子中: - `dp[i]` 表示第 i 个斐波那契数。 - 初始条件为 `dp[0] = 0`, `dp[1] = 1`。 - 状态转移方程为 `dp[i] = dp[i - 1] + dp[i - 2]`。 --- #### 示例:0/1 背包问题 另一个经典问题是 0/1 背包问题,在这个问题中我们需要决定哪些物品放入容量有限的背包中以获得最大价值。下面是该问题的一个简单实现[^5]。 假设有一个重量数组 `weights` 和对应的值数组 `values`,背包的最大承重为 `maxWeight`。 ```javascript function knapsack(weights, values, maxWeight) { const n = weights.length; const dp = Array.from({ length: n + 1 }, () => Array(maxWeight + 1).fill(0)); for (let i = 1; i <= n; i++) { // 遍历每个物品 for (let w = 0; w <= maxWeight; w++) { // 遍历每种可能的重量 if (w >= weights[i - 1]) { dp[i][w] = Math.max( dp[i - 1][w], // 不选当前物品 dp[i - 1][w - weights[i - 1]] + values[i - 1] // 选择当前物品 ); } else { dp[i][w] = dp[i - 1][w]; // 当前物品太重,无法选择 } } } return dp[n][maxWeight]; // 返回最大价值 } // 测试用例 const weights = [2, 3, 4, 5]; const values = [3, 4, 5, 6]; const maxWeight = 5; console.log(knapsack(weights, values, maxWeight)); // 输出 7 ``` 在这个例子中: - `dp[i][w]` 表示考虑前 i 件物品且总重量不超过 w 的情况下可以获得的最大价值。 - 状态转移方程为 `dp[i][w] = Math.max(dp[i - 1][w], dp[i - 1][w - weight[i]] + value[i])`。 --- #### 性能分析 动态规划的时间复杂度通常由两个因素决定:状态的数量和每次状态转移所需的计算量。以上述背包问题为例,时间复杂度为 O(n * W),其中 n 是物品数量,W 是背包的最大承重[^2]。 尽管动态规划的空间复杂度较高,但可以通过滚动数组等方式优化空间消耗[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值