Задачи
February 29

Задача. Комбинированная сумма

Задача. Дан массив чисел-кандидатов и целевое число. Необходимо найти все уникальные комбинации в кандидатах, в которых сумма чисел-кандидатов равна целевому числу.

При этом каждое число в кандидатах может быть использовано в комбинации только один раз.

Примечание: Набор решений не должен содержать дублирующихся комбинаций.

Пример:

Input:
- [2,5,2,1,2], target = 5
Output:
- [[1,2,2], [5]]

Разбор

Это одна из классических задач, которые решаются методом поиска с возвратом (backtracking).

Исходный массив предварительно нужно отсортировать, это необходимо, чтобы мы могли легко убирать дублирующие элементы из комбинациий.

Суть алгоритма в использовании рекурсивной функции, где параметрами будут исходный массив, текущая комбинация элементов, текущий индекс, с которого мы делаем новый перебор.

Также мы будем передавать текущую сумму элементов из комбинации, чтобы нам не приходилось суммировать саму комбинацию в каждой итерации.

В рекурсивной функции точками выхода будут 2 условия:

  • Сумма больше целевого числа.
  • Сумма равна целевому числу: записываем в результируюший список текущую комбинацию элементов.

Далее будем перебирать элементы от индекса из параметров функции до конца исходного массива. Чтобы решение не содержало дублирующих комбинаций, пропустим повторяющие элементы.

И наконец сам алгоритм поиска с возвратом:

  • мы делаем предположение для текущего элемента и добавляем его в текущую комбинацию и рекурсивно вызываем эту же функцию.
  • После мы убираем последний элемент из текущей комбинации, тем самым мы проверили наше предположение для текущего элемента.

Смотрите детали реализации ниже.

Реализация

public static IList<IList<int>> CombinationSum(int[] candidates, int target) {
    Array.Sort(candidates);
    Target = target;
    Output = new List<IList<int>>();
    
    Backtracking(candidates, new List<int>(), 0, 0);
    
    return Output;
}

public static void Backtracking(int[] input, List<int> curr, int index, int sum)
{   
    if (sum > Target)
    {
        return;
    }

    if (sum == Target) {
        Output.Add(new List<int>(curr));
        return;
    }

    for (int i = index; i < input.Length; i++)
    {
        if (i > index && input[i] == input[i - 1])
            continue; // пропускаем дубликаты

        curr.Add(input[i]);
        
        Backtracking(input, curr, i + 1, sum + input[i]);
        
        curr.RemoveAt(curr.Count - 1);
    }
}

https://gist.github.com/unilecs/dac3557ea2bcff8760f68b233070f284

Play-test

https://dotnetfiddle.net/kue32m