动态规划
动态规划
- 算法设计的一种思想
- 将问题分解为相互重叠的子问题,对子问题进行求解,从而解决原问题
1. 动态规划和分治
- 区别
- 分治的子问题,相互独立,互不干扰
- 动态规划的子问题相互重叠
- 如斐波那契求和:f(n)=f(n-1)+f(n-2):f(n-1) 和f(n-2) 之间是不是存在联系(f(n-1)=f(n-2)+f(n-3))
LeetCode
70. 爬楼梯
假设你正在爬楼梯。需要 n
阶你才能到达楼顶。
每次你可以爬 1
或 2
个台阶。你有多少种不同的方法可以爬到楼顶呢?
示例 1:
输入:n = 2 输出:2 解释:有两种方法可以爬到楼顶。 1. 1 阶 + 1 阶 2. 2 阶
示例 2:
输入:n = 3 输出:3 解释:有三种方法可以爬到楼顶。 1. 1 阶 + 1 阶 + 1 阶 2. 1 阶 + 2 阶 3. 2 阶 + 1 阶
提示:
1 <= n <= 45
1. 看问题的类型,明显不是让你求出一个确定的值,也不是对原数据进行改造 2. 求最大的爬楼梯方法,递归 3. 转换为小问题,爬n阶,==>爬n-1阶,n-2阶 4. 递归出口 n=1 1,n=2 2
/** * @param {number} n * @return {number} */ var climbStairs = function(n) { // i*1+j*2=n i和j在来一个全排列 i个位置填1 j个位置填2 // const jMax=n/2; // for(let j=0;j<jMax;j++){ // const iCurr=n-2*jMax // // 知道了当前j和i的个数 // } // 爬上n阶楼梯 // 两种情况,爬上(n-1)阶+1阶 // 爬上(n-2)阶+2阶 // 则爬上n阶楼梯的方法数为 // 爬上n-1阶的方法数+n-2阶的方法数 // 前缀和 if(n<2){ return 1 } const dp=[1,1]; for(let i=2;i<=n;i++){ dp[i]=dp[i-1]+dp[i-2] } return dp[n] };
198. 打家劫舍
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
示例 1:
输入:[1,2,3,1] 输出:4 解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。 偷窃到的最高金额 = 1 + 3 = 4 。
示例 2:
输入:[2,7,9,3,1] 输出:12 解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。 偷窃到的最高金额 = 2 + 9 + 1 = 12 。
提示:
1 <= nums.length <= 100
0 <= nums[i] <= 400
1. 能偷的最大金额,很明显这个最大值需要经过枚举才可以看出(肉眼不能看出),明显的划归 2. 偷n阶房屋,==> 偷n-1房屋(VS)偷n-2房屋+当前房屋 3. 递归出口 n=1 nums[0] 金额
/** * @param {number[]} nums * @return {number} */ var rob = function(nums) { // n阶房屋 // 能偷的最大金额,n-2阶房屋的最大金额+当前房屋(n)的金额 // 或者 n-1 阶房屋的最大金额 // 递归出口 n=0 0,n=1 nums[0], n=2 max(nums[0],nums[1]) if(!nums.length){ return 0 } if(nums.length===1){ return nums[0] } const dp=[0,nums[0]] for(let i=2;i<=nums.length;i++){ dp[i]=Math.max(dp[i-2]+nums[i-1],dp[i-1]) } return dp[nums.length] };
- 注意点
1. 这里面空间复杂度还可以优化 2. 没必要存储一个dp数组,我们要的是顶部元素
213. 打家劫舍 II
你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。
给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,今晚能够偷窃到的最高金额。
示例 1:
输入:nums = [2,3,2] 输出:3 解释:你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。
示例 2:
输入:nums = [1,2,3,1] 输出:4 解释:你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)。 偷窃到的最高金额 = 1 + 3 = 4 。
示例 3:
输入:nums = [1,2,3] 输出:3
提示:
1 <= nums.length <= 100
0 <= nums[i] <= 1000
1. 和第一版的打家劫舍相同,只是不能同时偷第一家和最后一家 2. 那我们把这个问题分解为偷0-(n-1)家 3. 和偷1-n家 4. 看那个能偷盗最多的金额
/** * @param {number[]} nums * @return {number} */ var rob = function(nums) { // n阶房屋 // 能偷的最大金额,n-2阶房屋的最大金额+当前房屋(n)的金额 // 或者 n-1 阶房屋的最大金额 // 递归出口 n=0 0,n=1 nums[0], n=2 max(nums[0],nums[1]) if(!nums.length){ return 0 } if(nums.length===1){ return nums[0] } if(nums.length===2){ return Math.max(nums[0],nums[1]) } // 比对偷0-(n-1)家的金额大 // 还是 1-n家的金额大 const nums1=nums.slice(0,nums.length-1) const nums2=nums.slice(1,nums.length) const dp1=[0,nums1[0]] for(let i=2;i<=nums1.length;i++){ dp1[i]=Math.max(dp1[i-2]+nums1[i-1],dp1[i-1]) } // 不偷第一家 const dp2=[0,nums2[0]] for(let i=2;i<=nums2.length;i++){ dp2[i]=Math.max(dp2[i-2]+nums2[i-1],dp2[i-1]) } return Math.max(dp1[nums1.length],dp2[nums2.length]) };
322. 零钱兑换
难度中等2157收藏分享切换为英文接收动态反馈
给你一个整数数组 coins
,表示不同面额的硬币;以及一个整数 amount
,表示总金额。
计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1
。
你可以认为每种硬币的数量是无限的。
示例 1:
输入:coins = [1, 2, 5], amount = 11 输出:3 解释:11 = 5 + 5 + 1
示例 2:
输入:coins = [2], amount = 3 输出:-1
示例 3:
输入:coins = [1], amount = 0 输出:0
提示:
1 <= coins.length <= 12
1 <= coins[i] <= 231 - 1
0 <= amount <= 104
完整代码
/** * @param {number[]} coins * @param {number} amount * @return {number} */ var coinChange = function(coins, amount) { // 使用类似爬楼梯的算法 // 爬到n层 // 需要最小的步数 // 即(n-coins[i])+coins[i] 步 // (n-coins[i]) 除了当前的硬币面额,已经爬了n-coins层的最小硬币数 // 关键在于i是可以变化的,(爬楼梯就不仅仅是n-1阶或者n-2阶了) // 定义一个数组,存储爬到第k层的最小硬币数,默认设置最大值,因为面额1的话,就是amount,amount+1自然最大值 let arr=new Array(amount+1).fill(amount+1) arr[0]=0 // i 为爬的层数 for(let i=0;i<=amount;i++){ // coin为一次爬coin步(比对爬楼梯一次只能1或者2) for(let coin of coins){ if(i-coin>=0){ // 到了这里说明爬的层数大于等于(一次爬的),可以使用一次道具(一次爬coin步) 看步数是否减少 arr[i]=Math.min(arr[i],arr[i-coin]+1) } } } return arr[amount]< amount+1? arr[amount]:-1 };
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!