记一次偶然看到的一个js题,自己起初很难理解,方便记忆记录一下,已熟知的请忽略
题目描述:给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1
示例1:
输入: coins = [1, 2, 5], amount = 11
输出: 3
解释: 11 = 5 + 5 + 1
示例2:
输入: coins = [2], amount = 3
输出: -1
答案是在这个
const coinChange = function (coins, amount) {
const f = [];
f[0] = 0;
for (let i = 1; i <= amount; i++) {
f[i] = Infinity;
for (let j = 0; j < coins.length; j++) {
if (i - coins[j] >= 0) {
// 状态转移方程
f[i] = Math.min(f[i], f[i - coins[j]] + 1);
}
}
}
if (f[amount] === Infinity) {
return -1;
}
return f[amount];
};
虽然看到答案也验证确实是这样,可是就是不理解,想要理解为什么记录下解析过程
状态转移方程,是动态规划中本阶段的状态往往是上一阶段状态和上一阶段决策的结果。如果给定了第K阶段的状态Sk以及决策uk(Sk),则第K+1阶段的状态Sk+1也就完全确定。
动态转移方程定义:动态规划中本阶段的状态往往是上一阶段状态和上一阶段决策的结果。若给定了第K阶段的状态Sk以及决策uk(Sk),则第K+1阶段的状态Sk+1也就完全确定。也就是说Sk+1与Sk,uk之间存在一种明确的数量对应关系,记为Tk(Sk,uk),即有Sk+1= Tk(Sk,uk)。 这种用函数表示前后阶段关系的方程,称为状态转移方程。在上例中状态转移方程为 Sk+1= uk(Sk) 。
刚看到这个解释是不明白的,后来越看越有味道,一下是菜鸟白话
我们可以输出一下f数组,这个数组记录了从0到amount的每一个的最小个数
当
coinChange([2],6)
时f数组是是
[ 0, Infinity, 1, Infinity, 2, Infinity, 3 ]
当amount=1时对应的f[1] Infinity
当amount=2对应f[2] 1
当amount=3对应f[3] Infinity
当amount=4对应f[4] 2
当amount=5对应f[5] Infinity
当amount=5对应f[6] 3
这个数组记录了每一个对应的值,要从上一个结果推出下一个结果就要找方法、找规律
i - coins[j] >= 0 就是判断给出的有比总和小的或相等数字,否则当然一个也没有。
f[i - coins[j]] 在数组中找到除去coins中的一个,余下值对应的个数,这个在f数组里自然有存着
Math.min 这个是找到除去coins中的一个,余下值对应最小的一个
f[i] 会在for (let j = 0; j < coins.length; j++)循环中被替换为最小的一个
+ 1 是因为除去了coins中的一个,计算总个数时要用余下的最小的值加上这个除去的这个。
因为f数组记录了所有,所以可以从中取推导。
多写几个数取推导取揣摩。