Skip to main content

Комбинация сумм

Описание

Дан набор чисел (candidates) (без дубликатов) и целевое число (target). Необходимо найти все уникальные комбинации чисел candidates, сумма которых равняется target.

Дополнительные условия:

  • одно и тоже число candidates может использоваться многократно
  • все числа (включая target) являются положительными
  • решение не должно содержать повторений

Примеры

Дано:
candidates = [2,3,6,7], target = 7,

Решение:
[
[7],
[2,2,3]
]
Дано:
candidates = [2,3,5], target = 8,

Решение:
[
[2,2,2,2],
[2,3,3],
[3,5]
]

Объяснение

Поскольку задача состоит в получении всех возможных результатов, а не лучшего результата и не количества результатов, нам не требуется динамическое программирование. Для решения задачи достаточно рекурсивного подхода.

Пример дерева решений для candidates = [2, 3] и target = 6:

                0
/ \
+2 +3
/ \ \
+2 +3 +3
/ \ / \ \
+2 ✘ ✘ ✘ ✓
/ \
✓ ✘

Реализация

// algorithms/sets/combination-sum.js
function combinationSumRecursive(
candidates,
remainingSum,
finalCombinations = [],
currentCombination = [],
startFrom = 0,
) {
if (remainingSum < 0) {
// Добавив кандидата, мы опустились ниже `0`.
// Это означает, что последний кандидат неприемлем
return finalCombinations
}

if (remainingSum === 0) {
// Если после добавления кандидата, мы получили `0`,
// нужно сохранить текущую комбинацию, поскольку
// это одно из искомых решений
finalCombinations.push(currentCombination.slice())

return finalCombinations
}

// Если мы пока не получили `0`, продолжаем добавлять оставшихся кандидатов
for (let i = startFrom; i < candidates.length; i++) {
const currentCandidate = candidates[i]

currentCombination.push(currentCandidate)

combinationSumRecursive(
candidates,
remainingSum - currentCandidate,
finalCombinations,
currentCombination,
i,
)

// Возвращаемся назад, исключаем текущего кандидата и пробуем другого
currentCombination.pop()
}

return finalCombinations
}

export default function combinationSum(candidates, target) {
return combinationSumRecursive(candidates, target)
}

Тестирование

// algorithms/sets/__tests__/combination-sum.test.js
import combinationSum from '../combination-sum'

describe('combinationSum', () => {
it('должен найти все комбинации чисел для получения указанной суммы', () => {
expect(combinationSum([1], 4)).toEqual([[1, 1, 1, 1]])

expect(combinationSum([2, 3, 6, 7], 7)).toEqual([[2, 2, 3], [7]])

expect(combinationSum([2, 3, 5], 8)).toEqual([
[2, 2, 2, 2],
[2, 3, 3],
[3, 5],
])

expect(combinationSum([2, 5], 3)).toEqual([])

expect(combinationSum([], 3)).toEqual([])
})
})

Запускаем тесты:

npm run test ./algorithms/sets/__tests__/combination-sum