← Назад к ДП

Задача о рюкзаке (0/1)

Выбор предметов с максимальной стоимостью при ограничении по весу

🐍 Python (максимальная стоимость)
W = int(input())  # вместимость рюкзака
n = int(input())  # количество предметов
w = [0] * (n + 1)
c = [0] * (n + 1)

for i in range(1, n + 1):
    w[i] = int(input())
for i in range(1, n + 1):
    c[i] = int(input())

dp = [[0] * (W + 1) for _ in range(n + 1)]

for i in range(1, n + 1):
    for j in range(1, W + 1):
        if j < w[i]:
            dp[i][j] = dp[i - 1][j]
        else:
            dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i]] + c[i])

print(dp[n][W])
⚙️ C++ (максимальная стоимость)
#include <bits/stdc++.h>
using namespace std;

int main() {
    int n, W;
    cin >> n >> W;
    
    vector<int> w(n + 1);
    for (int i = 1; i <= n; i++) {
        cin >> w[i];
    }
    
    vector<int> c(n + 1);
    for (int i = 1; i <= n; i++) {
        cin >> c[i];
    }
    
    vector<vector<int>> dp(n + 1, vector<int>(W + 1, 0));
    
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= W; j++) {
            if (j < w[i]) {
                dp[i][j] = dp[i - 1][j];
            } else {
                dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i]] + c[i]);
            }
        }
    }
    
    cout << dp[n][W];
    return 0;
}
🐍 Python (восстановление ответа)
W = int(input())  # вместимость рюкзака
n = int(input())  # количество предметов
A = [int(input()) for _ in range(n)]  # веса предметов

dp = [[0] * (W + 1) for _ in range(n + 1)]
dp[0][0] = 1

for i in range(1, n + 1):
    for j in range(W + 1):
        if A[i - 1] <= j:
            dp[i][j] = dp[i - 1][j] or dp[i - 1][j - A[i - 1]]
        else:
            dp[i][j] = dp[i - 1][j]

if dp[n][W] == 0:
    print(0)
    exit()

Z = []
i, j = n, W
while i != 0 or j != 0:
    if dp[i - 1][j] == 1 and (j >= A[i - 1] and dp[i - 1][j - A[i - 1]] == 1):
        print(-1)
        exit()
    else:
        if dp[i - 1][j] == 1:
            i -= 1
        else:
            i -= 1
            j -= A[i]
            Z.append(i + 1)

if len(Z) == 0:
    print(0)
else:
    print(*list(reversed(Z)))
⚙️ C++ (восстановление ответа)
#include <bits/stdc++.h>
using namespace std;

int main() {
    int W, n;
    cin >> W >> n;
    
    vector<int> A(n);
    for (int i = 0; i < n; i++) {
        cin >> A[i];
    }
    
    vector<vector<bool>> dp(n + 1, vector<bool>(W + 1, false));
    dp[0][0] = true;
    
    for (int i = 1; i <= n; i++) {
        for (int j = 0; j <= W; j++) {
            if (A[i - 1] <= j) {
                dp[i][j] = dp[i - 1][j] || dp[i - 1][j - A[i - 1]];
            } else {
                dp[i][j] = dp[i - 1][j];
            }
        }
    }
    
    if (!dp[n][W]) {
        cout << 0;
        return 0;
    }
    
    vector<int> Z;
    int i = n, j = W;
    while (i != 0 || j != 0) {
        if (dp[i - 1][j] && (j >= A[i - 1] && dp[i - 1][j - A[i - 1]])) {
            cout << -1;
            return 0;
        } else {
            if (dp[i - 1][j]) {
                i--;
            } else {
                i--;
                j -= A[i];
                Z.push_back(i + 1);
            }
        }
    }
    
    reverse(Z.begin(), Z.end());
    for (int x : Z) {
        cout << x << " ";
    }
    
    return 0;
}

Как работает задача о рюкзаке (0/1)?

Задача о рюкзаке (Knapsack problem) — это классическая задача ДП, в которой нужно выбрать предметы с максимальной суммарной стоимостью, не превышая заданную вместимость рюкзака.

Название "0/1" означает, что каждый предмет можно либо взять (1), либо не брать (0) — дробные части не допускаются.

Основная идея динамического программирования

Пусть dp[i][j] — максимальная стоимость, которую можно получить, используя первые i предметов и вместимость j.

Рекуррентная формула:

где w[i] — вес i-го предмета, c[i] — его стоимость.

Базовый случай: dp[0][j] = 0 (без предметов стоимость 0).

Временная сложность: O(n × W) — два вложенных цикла.

Пространственная сложность: O(n × W) — матрица dp.

Восстановление ответа

Для нахождения набора предметов, дающих максимальную стоимость, используется обратный проход по таблице dp:

Пример работы

Рассмотрим рюкзак вместимостью W = 10 и 4 предмета:

Предмет 1: вес = 6, стоимость = 30
Предмет 2: вес = 3, стоимость = 14
Предмет 3: вес = 4, стоимость = 16
Предмет 4: вес = 2, стоимость = 9

Заполнение таблицы dp:

     0   1   2   3   4   5   6   7   8   9   10
0    0   0   0   0   0   0   0   0   0   0   0
1    0   0   0   0   0   0  30  30  30  30  30
2    0   0   0  14  14  14  30  30  30  44  44
3    0   0   0  14  16  16  30  30  30  44  46
4    0   0   9  14  16  25  30  30  39  44  46

Ответ: dp[4][10] = 46

Максимальная стоимость: 46
Набор предметов: 2 + 3 + 4 = 14 + 16 + 9 = 39? нет, 2+4 = 14+9=23...
Оптимальный набор: предметы 1 и 4 (вес 6+2=8, стоимость 30+9=39) или предметы 2,3,4 (вес 3+4+2=9, стоимость 14+16+9=39) — но в таблице 46?
Проверим: предметы 1 и 3? вес 6+4=10, стоимость 30+16=46! Да, это оптимально.

Оптимальный набор: предметы 1 и 3 (вес 10, стоимость 46)

Попробовать алгоритм онлайн

Введите параметры задачи и найдите максимальную стоимость:

Результат появится здесь...