Перейти в раздел книги

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

Основными вопросами, которые при этом изучаются, являются следующие:

  • Как подсчитать количество элементов в множестве М?

  • Как эффективно (под эффективностью в этой главе мы понимаем скорость работы программы, а не просто существование полиномиального алгоритма, как обычно) перечислить (сгенерировать) все элементы множества М, каждое ровно один раз?

  • Пусть на множестве М определен некоторый порядок. Как эффективно перечислить элементы М именно в этом порядке?

  • Как по объекту х М получить следующий или предыдущий в заданном порядке? Как определить порядок, чтобы соседние объекты отличались как можно меньше?

  • Как по объекту х М найти его номер для заданного порядка, и наоборот, по номеру - элемент? Как определить порядок, чтобы эти операции выполнялись эффективно?

Хорошим введением в очерченный круг вопросов может служить глава 1 книги [Липский 88]. Также порекомендуем главу 2 книги [Шень95]. Для обучения искусству составления комбинаторных  алгоритмов  авторы устраивают так называемые блиц-туры по комбинаторике (пример такого блиц-тура приведен в 5.1). Они заключаются в максимально быстром решении как можно большего количества комбинаторных задач. Среди этих задач даются как стандартные задачи, вроде генерирования перестановок, так и нестандартные, вроде подсчета разбиений числа N на сумму от К до L слагаемых, каждое из которых принадлежит диапазону от А до В. Первые из них необходимы для выработки автоматизма при программировании наиболее часто встречающихся комбинаторных алгоритмов, вторые - для развития умения придумывать такого рода алгоритмы самостоятельно

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

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