РУҚА
Тапсырмалар № 26, 27 · ЕГЭ

Шаблон алгоритма с возвратом

Универсальная схема перебора: выбрать вариант, проверить допустимость, продолжить поиск и отменить выбор
6 мин чтенияҚиындық: Обновлено 29 қыркүйек 2026

Бэктрекинг (также пишут «бектрекинг») — это шаблон рекурсивного перебора, в котором алгоритм делает выбор, проверяет его, углубляется дальше, а при неудаче отменяет выбор и пробует следующий вариант. Схема особенно полезна в задачах на построение последовательностей, маршрутов, расстановок и состояний.

Идея и состояние перебора

Перед написанием алгоритма нужно точно определить, что хранит один рекурсивный вызов. Это называется состоянием перебора. Например, при построении последовательности это уже выбранные элементы, при движении по клеткам — текущая клетка и посещённые клетки, а при обработке автомата — текущее состояние и прочитанный префикс.

D
Определение

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

Обычно рекурсивная процедура состоит из четырёх действий: выбрать очередной вариант; проверить ограничения; добавить выбор в состояние; вызвать процедуру для следующего шага. После возврата из рекурсии выбор нужно удалить. Последнее действие обязательно: именно оно освобождает состояние для следующей ветви.

\[\text{выбрать} \rightarrow \text{проверить} \rightarrow \text{углубиться} \rightarrow \text{отменить выбор}\]
T
Правило корректности

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

Универсальный шаблон

Пусть \(state\) — текущее состояние, а \(pos\) — номер шага. Множество кандидатов зависит от состояния и номера шага. Условие допустимости отбрасывает ветви как можно раньше: не нужно строить продолжение, которое уже нарушает ограничения.

Псевдокод
1procedure Search(state, pos):
2    if complete(state, pos):
3        process(state)
4        return
5
6    for choice in candidates(state, pos):
7        if allowed(state, choice, pos):
8            apply(state, choice, pos)
9            Search(state, pos + 1)
10            undo(state, choice, pos)

В задачах на поиск бір шешімдер вместо process(state) можно вывести ответ и завершить работу. Если требуется посчитать решения, увеличивают счётчик. Если нужно вывести все решения, обработка выполняется для каждой достигнутой конечной вершины.

Приём для проверки

Для каждой переменной состояния задайте сұрақ: когда она изменяется и где восстанавливается? Если изменение есть, а обратного действия нет, вероятна ошибка в бэктрекинге.

База рекурсии и отмена выбора

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

\[T(state,pos)=\begin{cases}\text{обработать решение},&\text{если выполнено условие завершения};\\\displaystyle\sum_{c\in C(state,pos)}T(state\cup\{c\},pos+1),&\text{иначе}.\end{cases}\]

Операция отмены должна быть обратной операции выбора. Если при выборе сделали used[x] = true, после рекурсивного вызова нужно сделать used[x] = false. Если увеличили счётчик ресурса, его нужно уменьшить; если добавили элемент в список, его нужно удалить.

!
Частая ошибка

Нельзя ставить отмену выбора перед рекурсивным вызовом: тогда следующий вызов не увидит выбранный элемент. Нельзя также забывать отмену в одной из ветвей. Надёжный порядок таков: изменить состояние — вызвать рекурсию — восстановить состояние.

Бэктрекинг часто изображают деревом: вершина — состояние, ребро — выбор, лист — полный вариант. При обходе дерево просматривается в глубину, поэтому полезно сравнить схему с поиском в глубину. Число вызовов связано с размером дерева рекурсивных вызовов; оценить его помогает бет размер дерева рекурсивных вызовов.

Микро-проверка

В каком месте шаблона нужно снять отметку used[x]?

Разобранный пример: перестановки

Построим все перестановки чисел \(1,2,3\). На позиции pos выбираем ещё не использованное число. Состояние состоит из массива a и отметок used. Когда заполнены три позиции, выводим массив.

№
Что проверяем

На каждом шаге допустим любой элемент из множества \(\{1,2,3\}\), если он ещё не использован. После выбора элемент помечается, а после возврата белгі снимается.

1
В начале ни одна позиция не заполнена и ни одно число не использовано.
\(\displaystyle pos=0,\quad used[1]=used[2]=used[3]=false\)
2
На первой позиции можно выбрать 1. После выбора продолжаем с позиции 1.
\(\displaystyle a[0]=1,\quad used[1]=true\)
3
На второй позиции доступны 2 и 3. Выберем 2.
\(\displaystyle a=[1,2,\_],\quad used[1]=used[2]=true\)
4
На третьей позиции остаётся 3; последовательность полна и выводится.
a=[1,2,3]
5
Возвращаемся на третий шаг, отменяем выбор 3, затем на втором шаге отменяем выбор 2. Пробуем 3.
\(\displaystyle a=[1,3,\_],\quad used[1]=used[3]=true\)
6
На последней позиции остаётся 2, поэтому получаем второе Шешім с началом 1.
a=[1,3,2]
Python
1a = [0, 0, 0]
2used = [False, False, False, False]
3
4
5def search(pos):
6    if pos == 3:
7        print(*a)
8        return
9
10    for x in range(1, 4):
11        if not used[x]:
12            a[pos] = x
13            used[x] = True
14            search(pos + 1)
15            used[x] = False
16
17
18search(0)

Первые ветви дают \(123\), \(132\), затем после отмены выбора первого элемента — \(213\), \(231\), \(312\), \(321\). Всего шешімдер \(3! = 6\). Важен не только вывод, но и восстановление массива отметок: после завершения ветви used[x] снова должен иметь значение false.

\[N_n=n!,\qquad N_3=3\cdot2\cdot1=6\]

Если ограничения зависят от предыдущих элементов, их проверяют в allowed. Например, при построении последовательности без одинаковых соседей жаңа элемент \(x\) допустим, если \(pos=0\) немесе \(x\ne a[pos-1]\). Для тапсырма о маршруте проверяют границы поля, запрет посещённой клетки и возможность перехода.

Реті нұсқа и отсечения

Порядок перебора не меняет множество найденных решений, но влияет на то, какое решение будет найдено первым и сколько времени займёт поиск. Вопрос выбора порядка разобран на странице порядок перебора нұсқа. В задачах с подсчётом всех вариантов порядок обычно не влияет на ответ, но может влиять на скорость.

Отсечение — это досрочное прекращение ветви, которая уже не может привести к цели. Условие отсечения должно быть безопасным: если оно истинно, ни одно продолжение этой ветви не является решением. Например, если сумма уже превысила допустимый предел, добавлять новые положительные числа бессмысленно.

T
Безопасное отсечение

Ветвь можно удалить без потери решений только тогда, когда доказано: ни одно её продолжение не удовлетворяет условию задачи. Проверка, которая лишь кажется полезной, может сделать ответ неверным.

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

Как распознать шаблон в есепке

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

В задачах о Кузнечике важно различать конечную клетку и переходы: страницу конечная клетка Кузнечика читают вместе с рекурсивной схемой маршрутов. В задачах об автоматах аналогично различают начальное состояние и условие принятия: пригодятся начальное состояние автомата и заключительное состояние автомата. Если рекурсивный вызов стоит последним действием, это уже отдельный случай — хвостовая рекурсия.

!
Частые ошибки

1. База проверяет не то условие: например, алгоритм останавливается при достижении нужной длины, но не проверяет сумму. 2. В цикл попадают недопустимые кандидаты. 3. Состояние не восстанавливается после возврата. 4. Один и тот же вариант учитывается несколько раз из-за неверного массива used. 5. Для подсчёта маршрутов выводят каждый маршрут вместо увеличения счётчика.

Жылдам тест

Q
Жылдам тест по теме

Проверь себя

~ 2 мин4 вопроса
Вопрос 1 / 4
Вопрос 1 из 4 · шаблон
Какое действие завершает обработку одной ветви и подготавливает следующую?
Главное за минуту

Главное

  • Бэктрекинг строится по схеме: выбрать, проверить, рекурсивно продолжить, отменить выбор.
  • Состояние должно полностью описывать текущую частичную конструкцию и восстанавливаться после каждой ветви.
  • База рекурсии обрабатывает полное Шешім или проверяет условие завершения.
  • Безопасное отсечение ускоряет поиск, но требует доказательства, что удалённая ветвь не содержит решения.
  • Для проверки алгоритма проследите одну ветвь вручную и убедитесь, что после возврата все отметки и счётчики восстановлены.