Я пытался решить проблему обмена монет. Я использовал два похожих фрагмента кода, но в результате один прошел, а другой выполнился по таймауту. Я хочу знать, почему эти два похожих фрагмента кода дают два разных результата, объясните, пожалуйста, принцип.
Ссылка на литкод этой проблемы: https://leetcode.cn/problems/coin-change/description/
Ввод превышает лимит времени:
монеты = [186,419,83,408]
сумма = 6249
1.Прошел один:
var coinChange = function(coins, amount) {
const filter = amount + 1;
const memo = new Array(amount + 1).fill(filter);
const dp = function(coins, amount) {
if (amount === 0) {
return 0;
}
if (amount < 0) {
return -1;
}
if (memo[amount] !== filter) {
return memo[amount];
}
let res = Infinity;
for (let coin of coins) {
let subAmount = dp(coins, amount - coin);
if (subAmount === -1) {
continue;
}
res = Math.min(res, subAmount + 1);
}
memo[amount] = (res === Infinity) ? -1 : res;
return memo[amount];
}
return dp(coins, amount);
}
var coinChange = function(coins, amount) {
const filter = amount + 1;
const memo = new Array(amount + 1).fill(filter);
const dp = function(amount) {
if (amount === 0) {
return 0;
}
if (amount < 0) {
return -1;
}
if (memo[amount] !== filter) {
return memo[amount];
}
for (let coin of coins) {
let subAmount = dp(amount - coin);
if (subAmount === -1) {
continue;
}
memo[amount] = Math.min(memo[amount], subAmount + 1);
}
return memo[amount] === filter ? -1 : memo[amount]
}
return dp(amount);
}
🤔 А знаете ли вы, что...
Синтаксис JavaScript схож с синтаксисом языка программирования Java, но они не связаны.
Основная проблема в том, что вы не всегда занимаетесь мемоизацией. Обе версии кода имеют особое значение filter
, указывающее, что значение еще не было сохранено. Но когда на определенную сумму решения нет, неисправный код не обновится memo[amount]
. Ну, будет, но с тем же значением, которое оно уже имело: filter
. Это означает, что вы не запомнили неудачу. В этом случае правильный код сохранит -1
.
Итак, исправление первой версии состоит в том, чтобы изменить это:
return memo[amount] === filter ? -1 : memo[amount]
к этому:
if (memo[amount] === filter) memo[amount] = -1;
return memo[amount];
С этим исправлением все будет работать.
Замечание: не рекомендуется использовать такое утверждение:
memo[amount] = Math.min(memo[amount], subAmount + 1);
...поскольку это еще не гарантирует правильный оптимизированный результат. Правильный результат станет известен только тогда, когда вы закончите цикл, перепробовав все возможные монеты. Только тогда вам следует сохранить окончательное значение в таблице мемоизации. К счастью, это не оказывает негативного влияния, поскольку все суммы, проверяемые в дереве более глубокой рекурсии, меньше этой суммы, поэтому memo[amount]
не будет учитываться, пока этот цикл все еще работает. Но кажется хорошей практикой хранить в таблице запоминания только окончательные (оптимизированные) значения.