Offer 驾到,掘友接招!我正在参与2022春招系列活动-刷题打卡任务,点击查看活动详情。
前言
每天一道算法题,死磕算法
最近做了一些动态规划的题目,总结了一下模板
-
1.定义新数组 动态规划题目一般需要定义一个新数组let a=[]或let a=[][]之类的
-
2.找到最简单的子问题
这个子问题一般都是a[0],a[1]
之类的,不可再分解的,这个其实就是递归的边界条件 -
3.找到父问题
要能看出来让你求什么,一般也就是数组的第几个值 -
4.找到如何把父问题化成子问题(自顶向下推理不出来,就自底向上推理试试) 根据题目中的条件,想一下子问题是如何构成父问题的,这是最关键的一步,也是最难的一步,这个只能多做
动态规划的一些思想
穷举
动态规划题目其实就是把每种情况都遍历出来,一定是全部情况,然后从中选取最优的
递归or遍历
自顶向下就是递归+记忆化搜索
自底向上就是遍历
两者都可以求出来最终答案,在面试的时候都算正确答案
动态规划注意边界问题
因为动态规划都是数组问题,所以一定要注意边界
题目
给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。
计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。
你可以认为每种硬币的数量是无限的。
示例 1:
输入:coins = [1, 2, 5], amount = 11 输出:3 解释:11 = 5 + 5 + 1
分析
第一步从题目中提取关键字
计算最少的硬币个数,是个数。里面有一个最少,那就是让求最优
的问题, 一看到这种最优解的问题里面往动态规划上想
这是一道求最值的动态规划
按照上面的模板开始一步一步实现
第一步:定义一个数组let arr = [];
第二步:寻找最简单的子问题?
是arr[1]=1么?当题目中给了你最小值了,那么你在-1就可以了,那就是arr[0]=0; 也就是一枚硬币也不需要,比如amount为0,那么返回0;
第三步:找到父问题是什么? 以示例一为例子,那么父问题就是arr[11]为多少?
第四部:子问题是如何构成父问题的?
例子一中条件是什么?
条件是coins
子问题是1,2,3,4,5,6,7,8,9,10,看看子问题是如何通过条件构父问题的呢
一般都是父问题减法得到子问题,你不减,怎么得到子问题呢,对吧
根据上面这个图,我们可以看出 要想求出父问题11,可以先求出子问题10,子问题9,子问题6,他们都在加一枚硬币就可以得出父问题11,因为我们要求的是得出父问题11的最小值,所以我们要从这几个数中拿到最小值就可以了
arr[11]=Math.min(arr[11-1]+1,arr[11-2]+1,arr[11-5]+1);
所以现在我们把11改成n
arr[n]=Math.min(arr[n-1]+1,arr[n-2]+1,arr[n-5]+1);
动态规划题型
动态规划题目大概有三类题型
- 计数(多少种方法)
- 求最值
- 求存在性(后面会讲到)
求最值的动态规划也是有模板的
求最小值,一般先声明res为最大值 求最大值,一般先声明res为最小值
把所有可能的情况遍历一遍
模板
以获取最小值为例子
// 初始化最大值
let res = Infinity;
for(let i=0;i<n;i++){
res = Math.min(res,根据条件写)
}
题解
var coinChange = function(coins, amount){
// 定义一个空数组
let arr = [];
// 最简单的值
arr[0] = 0;
// 求出父问题前面的所有子问题
for(let i=1;i<=amount;i++){
// 求最小值一定要先声明最大值
let res = Infinity;
// 遍历所有的子条件
for(let j=0;j<coins.length;j++){
// 数组一定要考虑边界问题
if(i-coins[j]>=0){
// 注意加一
res = Math.min(res,arr[i-coins[j]]+1);
}
}
arr[i] = res;
}
return a[amount]===Infinity?-1:a[amount];
}
总结
我感觉动态规划是目前接触到题型中,灵活性最强的题型,但是只要咱们总结出模板以不变应万变
总是可以轻松掌握这个题型的,加油💪🏻jym(倔友们)