Пара слов о моем алгоритме. Поход примерно как у всех - нагенерировать множество наборов, у которых произведение мало отличается от целевого, а потом из них выбрать
штук так, что у каждой пары наборов ровно одно общее число.
Список оптимизаций такой:
1) Наборы храним в виде битовых полей. Бит с номером
выставлен, если в наборе присуствует простое число с номером
,
. Проверка, что ровно одно общее число у двух наборов, делается за несколько тактов:
bool intersects(uint64_t x, uint64_t y)
{
uint64_t z = x & y;
return z && !(z & (z-1));
}
Для
у нас уже больше 64 простых чисел, и на приходится использовать два 64-битных числа для одного набора, соответственно, чуть усложняется проверка.
Генерировать матрицу смежности было бы невыгодно(хотя я не проверял), потому что она огромная, даже в L3 не влезет, а постоянно тянуть из RAM - накладно.
2) Число два - это самое редко встречающееся в наборах число. Поэтому выгоднее стартовать с набора, содержащего двойку, а потом уже подбирать ему соседей.
3) Начало рекурсивного алгоритма таково: выбираем один набор (назовем его главным), содержащий двойку. Пусть набор состоит из чисел
. Формируем
множеств наборов (из тех, что мы нагенерировали в самом начале) таких, что
-ый набор содержит число
, а больше общих чисел с главным набором нет. Остальная часть алгоритма состоит в том, чтобы выбрать по одному представителю из этих
множеств, чтобы они попарно имели ровно одно общее число.
4) Общая итерация рекурсивного алгоритма такова: пусть на очередном этапе имеется
наборов, один из которых главный, у каждой пары из которых ровно одно общее число. Также у нас имеется
множеств, в которых содержатся кандидаты на оставшиеся
вершин. Мы смотрим, в каком из множеств меньше всего наборов. Если в нем 0 наборов, то возвращаемся на предыдущий уровень рекурсии. Иначе, в цикле перебираем каждого кандидата
из этого множества, вычисляем, какие числа у него общие с имеющимися
наборами - всеми, кроме главного (пусть это числа
), фильтруем оставшиеся
множеств наборов так, чтобы:
а) у каждого набора из множества было ровно одно общее число с
.
б) это общее число не было одним из
.
И переходим на следующий уровень рекурсии.
Дополнительно следим, чтобы сумма
(см
сообщение #1253678 ) не превышала лимит. Потому как, если на каком-то этапе рекурсии лимит превышен, то какие наборы не добавляй к имеющимся, решение оптимальным не будет. В принципе, можно и не следить за этой суммой, тогда мы будем получать неоптимальные решения, но поиск оптимального сильно затянется.
Значит, перебирая в пункте 3 все наборы, содержащие двойку и запуская рекурсивный алгоритм для каждого из них, мы рано гарантированно найдем оптимальное решение.