Один прямой путь - это рекурсивная процедура, которая выполняет следующие действия при каждом вызове. Входные данные для процедуры - это список пар, которые уже были выбраны, и список всех пар.
- Вычислите наименьшее число, которое еще не включено в список ввода. Для первого вызова это будет 0, конечно, потому что пары не были выбраны.
- Если все числа покрыты, у вас есть правильная комбинация, распечатайте ее и верните предыдущий шаг. В противном случае наименьшее число, которое будет обнаружено, является целью, к которой мы будем стремиться.
- Ищите пары, ища способ покрыть целевое число. Если его нет, просто вернитесь к предыдущему уровню рекурсии.
- Если есть способ покрыть целевое число, выберите первый способ и рекурсивно вызовите всю процедуру снова, с только что выбранной парой добавьте в список выбранных пар.
- Когда это вернется, ищите следующий способ, чтобы покрыть целевое число парой, не перекрывая ранее выбранную пару. Если вы найдете один, выберите его и снова рекурсивно вызовите следующую процедуру.
- Продолжайте шаги 4 и 5, пока не будет больше способов покрыть целевое число. Просмотрите весь список пар. Когда больше нет правильного выбора, вернитесь к предыдущему уровню рекурсии.
Способ визуализации этого алгоритма заключается в дереве, пути которого представляют собой последовательности неперекрывающихся пар. Первый уровень дерева содержит все пары, которые содержат 0. Для приведенного выше примера дерево
корень
|
----------------
| | |
(0,1) (0,2) (0,3)
| | |
(2,3) (1,3) (1,2)
В этом примере все пути через дерево фактически дают правильные коллекции, но, например, если бы мы пропустили пару (1,2), то самый правый путь имел бы только один узел и соответствовал бы неудаче поиска на шаге 3.
Поисковые алгоритмы этого типа могут быть разработаны для многих похожих задач перечисления всех объектов определенного типа.
Было высказано предположение, что, возможно, ОП означало, что на входе присутствуют все пары, а не только их набор, как говорится в вопросе. В этом случае алгоритм намного проще, потому что больше нет необходимости проверять, какие пары разрешены. Нет необходимости создавать множество всех пар; следующий псевдокод будет делать то, что спросил OP. Здесь - входной номер, «список» начинается с пустого списка, а «охватываемый» - это массив длиной n, инициализированный равным 0. Его можно сделать несколько более эффективным, но это не является моей непосредственной целью.nn
sub cover {
i = 0;
while ( (i < n) && (covered[i] == 1 )) {
i++;
}
if ( i == n ) { print list; return;}
covered[i] = 1;
for ( j = 0; j < n; j++ ) {
if ( covered[j] == 0 ) {
covered[j] = 1;
push list, [i,j];
cover();
pop list;
covered[j] = 0;
}
}
covered[i] = 0;
}