Рюкзак Аладдіна#1661

Рюкзак Аладдіна (Розбір)

Нехай dp_k[s] — максимальна вартість вантажу вагою не більше , якщо дозволено використовувати лише предмети перших типів.

Розглянемо два можливих варіанти при складанні вантажу вагою :

dp_k[i] = dp_{k - 1}[i]
dp_k[i] = dp_k[i - w_k] + v_k.

Оскільки потрібно максимізувати вартість вантажу, отримуємо рекурентне співвідношення:

dp_k[i] = max(dp_{k - 1}[i], dp_k[i - w_k] + v_k), w_k \le i \le s

Базові випадки:

dp_0[i] = 0~і~dp_k[0] = 0

Нехай функція f(k, s) обчислює максимальну вартість вантажу, який можна зібрати в рюкзак вагою не більше , використовуючи перші типів предметів. Тоді має місце рекурентне співвідношення:

f(k, s) = max(f(k - 1, s), f(k, s - w[k]) + v[k])

Залишилося обчислити значення функції f(k, s) використовуючи мемоізацію.

Реалізація алгоритму

Оголосимо робочі масиви:

#define MAXW 252
#define MAXN 37
int w[MAXN], p[MAXN];
int dp[MAXW];

Читаємо вхідні дані.

scanf("%d %d", &s, &n);
for (i = 0; i < n; i++)
  scanf("%d %d", &w[i], &p[i]);

Послідовно обробляємо типів предметів.

for (k = 0; k < n; k++)
{

Перераховуємо значення масиву , враховуючи можливість використання предметів типу . Оскільки кількість предметів кожного типу не обмежена, кожен предмет можна вибирати декілька разів.

  for (i = w[k]; i <= s; i++)
    dp[i] = max(dp[i], dp[i - w[k]] + p[k]);
}

Виводимо відповідь.

printf("%d\n", dp[s]);

Реалізація алгоритму — рекурсія

Оголосимо робочі масиви:

#define MAXW 252
#define MAXN 37
#define INF 2000000000
int w[MAXN], v[MAXN];
int dp[MAXN][MAXW];

Функція f(k, s) обчислює максимальну вартість вантажу, який можна зібрати в рюкзак вагою не більше , використовуючи перші типів предметів.

int f(int k, int s)
{

Якщо k = 0 або s = 0, то рюкзак порожній, і його вартість дорівнює .

  if (k == 0 || s == 0) return 0;

Якщо s < 0, то ми перевищили допустиму вагу, і цей варіант не має сенсу. Тому повертаємо  ( — це деяке велике число).

  if (s < 0) return -INF;

Якщо значення f(k, s) вже обчислено, то повертаємо його.

  if (dp[k][s] != -1) return dp[k][s];

Обчислюємо значення f(k, s) і запам'ятовуємо його в dp[k][s].

  return dp[k][s] = max(f(k - 1, s), f(k, s - w[k]) + v[k]);
}

Основна частина програми. Читаємо вхідні дані.

scanf("%d %d", &s, &n);
for (i = 1; i <= n; i++)
  scanf("%d %d", &w[i], &v[i]);

Обчислюємо і виводимо відповідь.

memset(dp, -1, sizeof(dp));
printf("%d\n", f(n, s));