Мы хотим заменить эту проблему на проблему, которая дает тот же ответ, но ее будет легче оценить.
Хитрость в том, чтобы упростить оценку с помощью подхода динамического программирования, состоит в том, чтобы одни и те же результаты появлялись во многих местах. И поэтому мы должны заменить эквивалентные версии этой проблемы на нормализованные.
Для начала ответ не зависит от порядка, в котором находятся элементы массива. Поэтому мы можем заменить наши массивы на массивы, отсортированные от наименьшего к наибольшему. Теперь мы добавляем x
ко всему, кроме одного, затем переупорядочиваем в каноническую форму.
Далее ответ не зависит от значения наименьшего элемента. Таким образом, мы можем вычесть это значение из всех записей. Теперь мы добавляем x
ко всему, кроме одного, затем переупорядочиваем в каноническую форму, затем вычитаем наименьшее из всего.
Это значительно уменьшает нашу проблему. Достаточно того, что поиск в ширину имеет шанс. Но у нас есть еще один трюк. И дело в том, что не имеет значения, в каком порядке мы применяем операции. И поэтому мы можем применить все наши 5 операций перед нашими 2 операциями перед нашими 1 операциями. С помощью этого трюка мы можем заменить каждый нормализованный узел на (node, last_operation)
и начальный last_operation
из 5. Причина, по которой это выигрыш, заключается в том, что теперь у нас есть верхняя граница для остальной части поиска A *. Эта граница является текущим числом шагов + сумма ceil(node[i] / last_operation)
.
И теперь это можно решить с помощью поиска *. Давайте сделаем ваши примеры вручную. Используя обозначения, (total cost, normalized, last_operation, steps)
.
Пример 1: [1, 1, 5]
Мы нормализуем до [0, 0, 4]
и имеем last_operation
5 и стоимость 0+0+1 = 1
. Никаких шагов не предпринято. Итак, начнем с:
(1, [0, 0, 4], 5)
Мы убираем это и рассматриваем наши действия. Для операции 5 получаем следующее:
[0, 0, 4] + [5, 5, 0] = [5, 5, 4] => [0, 1, 1] # cost 1 + 0+1+1 = 3
[0, 0, 4] + [5, 0, 5] = [5, 0, 9] => [0, 5, 9] # cost 1 + 0+1+2 = 4
[0, 0, 4] + [0, 5, 5] = [0, 5, 9] => [0, 5, 9] # cost 1 + 0+1+2 = 4 DUP
А для операции 2 получаем:
[0, 0, 4] + [2, 2, 0] = [2, 2, 4] => [0, 0, 2] # cost 1 + 0+0+1 = 2
[0, 0, 4] + [2, 0, 2] = [2, 0, 4] => [0, 2, 4] # cost 1 + 0+1+2 = 4
[0, 0, 4] + [0, 2, 2] = [0, 2, 4] => [0, 2, 4] # cost 1 + 0+1+2 = 4 DUP
А для операции 1 получаем:
[0, 0, 4] + [1, 1, 0] = [1, 1, 4] => [0, 0, 3] # cost 1 + 0+0+3 = 4
[0, 0, 4] + [1, 0, 1] = [1, 0, 4] => [0, 1, 4] # cost 1 + 0+1+5 = 6
[0, 0, 4] + [0, 1, 1] = [0, 1, 4] => [0, 1, 4] # cost 1 + 0+1+5 = 6 DUP
Мы помещаем 7 не дублирований в нашу приоритетную очередь, и лучшее, что получается, выглядит следующим образом:
(total cost, normalized, last_operation, steps)
( 2, [0,0,2], 2, 1)
Затем мы попробуем выполнить операции 2
и 1
и, разумеется, обнаружим, что одним из результатов будет [0, 0, 0]
после 2 шагов.