【发布时间】:2021-11-09 08:58:09
【问题描述】:
我正在编写一个函数来查找进行一定数量的零钱所需的最少硬币数量。 See this problem
这是一个动态编程问题,但是,我没有使用传统的数组,而是尝试使用一个对象来记忆结果。它叫coinAmounts。
该地图包含一个由最少数量的硬币组成的数组以产生特定的总和,例如,要产生 6 的数量,您需要 [5,1]。
我有一个 getCoinsToMakeAmount 和 setCoinsToMakeAmount 来编辑/获取地图中的值。当我们迭代路径时,setCoins 将更新我们的地图,如果我们通过它的路径是一个较小的硬币组合以获得给定的总和。
基本方法是回溯算法,然后我使用coinMap 来记忆总和。
编辑
我相信我错过了优化,因为该算法似乎适用于较小的循环,但是对于这样的较大输入,它似乎不是很好。
coins = [3,7,405,436]
sum = 8839
let combos = []
let coinAmounts = {}
let cycles = 0;
/**
* @param {number[]} coins
* @param {number} amount
* @return {number}
*/
var coinChange = function(coins, amount) {
coinAmounts = {}
combos = []
if(amount === 0) return 0;
coinChangeHelper(coins, 0, [], amount,0);
console.log(coinAmounts)
//console.log(combos);
// console.log("CYCLES: " + cycles)
if(!coinAmounts[amount]) return -1;
return coinAmounts[amount].length;
};
var coinChangeHelper = function(coins, amount, _chosen, desiredAmount, minPath){
let chosen = Object.assign([], _chosen);
cycles++;
// save the current iteration if it's better
setLowestCoinsToMakeAmount(amount, chosen);
if(amount > desiredAmount){
return false;
}
if(amount === desiredAmount){
combos.push(chosen);
return true;
}
// see if we already know how to make the amount requested
// need to do something with this chosen coins
const chosenCoins = getCoinsToMakeAmount(amount);
if(chosenCoins && chosenCoins.length < chosen.length){
return true;
}
//debug(coins, amount, chosenCoins, _chosen)
// like a backtracking algo
for(let i = minPath; i<coins.length; i++){
const coinAmount = coins[i];
// choose a coin
chosen.push(coinAmount)
coinChangeHelper(coins,amount+coinAmount, chosen, desiredAmount, i);
// unchoose the coin
chosen.pop();
}
}
// get a coin value
const getCoinsToMakeAmount = (amount, startCoin) => {
const currentCoinCount = coinAmounts[amount];
if(!currentCoinCount){
return undefined;
}
return currentCoinCount;
}
const setLowestCoinsToMakeAmount = (coinsSum, coinsChosen) =>{
if(coinsChosen.length <= 0) return;
const currentCoinCount = coinAmounts[coinsSum];
if(!currentCoinCount){
coinAmounts[coinsSum] = coinsChosen;
return;
}
if(coinsChosen.length < currentCoinCount.length){
coinAmounts[coinsSum] = coinsChosen;
}
}
// nicely print out the output
var debug = (coins, amt, memo, chosen) => {
let tabs = ''
for(let i =0; i<amt; i++){
tabs = tabs+'\t';
}
console.log(`${tabs} change([${amt}], [${memo}],[${chosen}]`)
}
对于 (coins=[1,2,5], amount=11) 的情况,coinAmounts 包含最佳硬币数量以达到一定的总和。
{
'1': [ 1 ],
'2': [ 2 ],
'3': [ 1, 2 ],
'4': [ 2, 2 ],
'5': [ 5 ],
'6': [ 1, 5 ],
'7': [ 2, 5 ],
'8': [ 1, 2, 5 ],
'9': [ 2, 2, 5 ],
'10': [ 5, 5 ],
'11': [ 1, 5, 5 ],
'12': [ 2, 5, 5 ],
'13': [ 1, 2, 5, 5 ],
'14': [ 2, 2, 5, 5 ],
'15': [ 5, 5, 5 ]
}
【问题讨论】:
标签: javascript dynamic-programming backtracking recursive-backtracking