Как ответить
Суть задачи о рюкзаке — выбрать подмножество предметов с заданными весом и стоимостью так, чтобы суммарный вес не превышал вместимость, а суммарная стоимость была максимальной. Выбор каждого предмета напрямую влияет на заполнение пространства: более тяжёлый предмет может вытеснить несколько лёгких, поэтому нужно балансировать между весом и ценностью. Самая распространённая версия — 0/1 рюкзак (каждый предмет берётся целиком или не берётся), решается динамическим программированием.
Реализация на Python для 0/1 рюкзака:
def knapsack_01(weights, values, capacity):
n = len(weights)
dp = [[0] * (capacity + 1) for _ in range(n + 1)]
for i in range(1, n + 1):
w = weights[i-1]
v = values[i-1]
for c in range(capacity + 1):
if w > c:
dp[i][c] = dp[i-1][c]
else:
dp[i][c] = max(dp[i-1][c], dp[i-1][c-w] + v)
return dp[n][capacity]
Ключевой момент: dp[i][c] — максимальная стоимость для первых i предметов при вместимости c. Выбор предмета i либо не меняет заполнение (если не брать), либо занимает w единиц пространства и добавляет v к стоимости (если взять). Перебор всех комбинаций неявный за счёт сохранения лучших результатов для каждой вместимости.
Влияние выбора на заполнение:
- Если предмет имеет большой вес и малую стоимость, его брать невыгодно — он занимает место, которое могли бы занять более ценные лёгкие предметы.
- При одинаковой стоимости на единицу веса жадный алгоритм (брать предметы с максимальной удельной стоимостью) даёт оптимальное решение только для дробного рюкзака, где можно брать части предметов. Для 0/1 жадность не гарантирует оптимум — нужно DP.
- Ограничение пространства создаёт конкуренцию: два предмета с суммарным весом ≤ capacity, но один из них может быть исключён, если вместе с другим даёт большую стоимость, чем третий тяжёлый предмет.
Пример: weights = [2,3,4], values = [3,4,5], capacity = 5. DP найдёт, что оптимально взять предметы с весом 2 и 3 (стоимость 7), а не один тяжёлый вес 4 (стоимость 5). Выбор первого предмета (вес 2) оставляет 3 единицы места, куда помещается второй предмет (вес 3), итого 5 — максимум.
На собеседовании важно показать понимание рекуррентной формулы, сложность O(n*capacity) по времени и памяти, а также возможность оптимизации памяти до O(capacity) с использованием одномерного массива (перебор вместимости справа налево для 0/1).