15.5. Задача о рюкзаке
Определение
Задача о рюкзаке (knapsack problem): есть рюкзак вместимости $W$ и $n$ предметов весов $w_1, \ldots, w_n$ и стоимостей $p_1, \ldots, p_n$. Найти подмножество предметов $A \subset \{1, \ldots, n\}$ максимальной суммарной стоимости, помещающееся в рюкзак: $\sum_{i \in A} w_i \leqslant W$.
Существует много различных вариаций этой задачи. Можно считать, что есть лишь одна копия каждого предмета (версия «без повторений»), а можно — что копий каждого предмета бесконечно много (версия «с повторениями»).
Версия «с повторениями»
Пусть $k[j]$ — максимальная возможная суммарная стоимость при вместимости рюкзака $j$. Начальное состояние: $k[0]=0$. Переход — выбор последнего взятого предмета:
$$ k[j] = \max_{w_i \leqslant j}\bigl(k[j - w_i] + p_i\bigr). $$Ответ на задачу: $\max_{0 \leqslant j \leqslant W} k[j]$. Получаем решение за $O(nW)$ с $O(W)$ дополнительной памяти.
Версия «без повторений»
Для того, чтобы не взять один предмет несколько раз, введём дополнительный параметр. Пусть $k[i, j]$ — максимальная возможная суммарная стоимость, если разрешается брать только предметы с номерами не больше $i$, а рюкзак имеет вместимость $j$. Начальные состояния: $k[0, j] = k[i, 0] = 0$. Переход: либо $i$-й предмет входит в ответ, либо нет.
Оптимизация памяти
При вычислении $k[i, j]$ нужны только значения $k[i-1, q]$ с $q \leqslant j$. Если перебирать $j$ в порядке убывания, можно обойтись одномерным массивом: при обработке состояния $(i, j)$ в $k[0..j]$ хранятся значения $k[i-1, 0], \ldots, k[i-1, j]$, а в $k[j+1..W]$ — значения $k[i, j+1], \ldots, k[i, W]$.
Свойства
Задача о рюкзаке NP-трудна в общем виде (зависимость от $W$), но алгоритм DP работает за $O(nW)$ — это псевдополиномиальное время, так как $W$ экспоненциально по числу битов в его представлении. Восстановление ответа в версии с одномерным массивом невозможно напрямую; однако можно применить идею алгоритма Хиршберга и восстановить ответ, используя $O(W + \log n)$ дополнительной памяти.