Lo zaino di Aladdin#1661

Lo zaino di Aladdin (Editoriale)

Sia dp_k[s] — il costo massimo di un carico di peso non superiore a , se è permesso utilizzare soltanto oggetti dei primi tipi.

Consideriamo due possibili varianti nella composizione di un carico di peso :

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

Poiché è richiesto massimizzare il costo del carico, otteniamo la relazione ricorrente:

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

Casi base:

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

Sia la funzione f(k, s) che calcola il costo massimo del carico che si può raccogliere in uno zaino di peso non superiore a , usando i primi tipi di oggetti. Allora vale la relazione ricorrente:

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

Resta da calcolare il valore della funzione f(k, s) usando la memoizzazione.

Implementazione dell'algoritmo

Dichiariamo gli array di lavoro:

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

Leggiamo i dati in ingresso.

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

Elaboriamo in sequenza gli tipi di oggetti.

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

Ricalcoliamo i valori dell'array , tenendo conto della possibilità di utilizzare oggetti di tipo . Poiché il numero di oggetti di ciascun tipo non è limitato, ogni oggetto può essere scelto più volte.

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

Stampiamo la risposta.

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

Implementazione dell'algoritmo — ricorsione

Dichiariamo gli array di lavoro:

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

La funzione f(k, s) calcola il costo massimo del carico che si può raccogliere in uno zaino di peso non superiore a , usando i primi tipi di oggetti.

int f(int k, int s)
{

Se k = 0 oppure s = 0, allora lo zaino è vuoto, e il suo costo è pari a .

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

Se s < 0, allora abbiamo superato il peso consentito, e questa variante non ha senso. Perciò restituiamo  ( — è un certo numero grande).

  if (s < 0) return -INF;

Se il valore f(k, s) è già stato calcolato, allora lo restituiamo.

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

Calcoliamo il valore f(k, s) e lo memorizziamo in dp[k][s].

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

Parte principale del programma. Leggiamo i dati in ingresso.

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

Calcoliamo e stampiamo la risposta.

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