3.1 KiB
Problema dello zaino
Il problema dello zaino è un problema pseudo-trattabile: non abbiamo dimostrazioni di se sia trattabile o intrattabile.
Descrizione
Sei un ladro, e devi mettere più refurtiva possibile nello zaino per scappare.
Lo zaino può portare al massimodim
kili.Quali (e quanti) oggetti scegli?
Input | Output |
---|---|
dim ensione_zaino, n umero_oggetti, oggetto.peso , oggetto.valore |
profitto_massimo |
Categorie
Problema con ripetizione
Puoi prendere tutte le copie che vuoi di un oggetto.
Soluzione
K(dim)
è il valore massimo ottenibile con uno zaino di capacità dim
.
Se
i
appartenesse alla soluzione ottima, alloraK(dim) = i.valore + K(dim - i.peso)
...
Possiamo dire che K(dim) = max(i.valore + K(dim - i.peso))
.
Inoltre, K(0) = 0
.
Ci salviamo tutte le soluzioni da K(0)
a K(dim)
, e le usiamo per calcolare il massimo in seguito.
Calcolare K(dim)
avrà allora un costo di O(n * dim)
:
n
, perchè trovare il massimo è un'operazione linearedim
, perchèdim
sono tutti i casi tra i quali devo andare a provare
Il costo computazionale, allora, è in O(n * dim)
.
Però, il tempo richiesto dal nostro algoritmo dipende non dalla lunghezza dell'input, bensì dal valore numerico di dim
, che corrisponde alla dimensione dell'array delle soluzioni.
Allora, si dice che l'algoritmo è in tempo pseudo-polinomiale.
Problema senza ripetizione
Si può prendere ogni oggetto una volta sola.
Soluzione bruteforce
Scelgo se prendere o no l'item 1.
Si creano due percorsi:
- Non prendo l'oggetto:
valore = 0, peso = 0
- Prendo l'oggetto:
valore = oggetto.valore, peso = oggetto.peso
Continuo a creare percorsi, creando una specie di albero binario.
Se a un certo punto vedo che valore = x, peso = K
e valore < x, peso = K
, allora posso escludere automaticamente tutto il sottoalbero destro, perchè non può essere migliore del sinistro: allora, sarò riuscito a ridurre il numero dei casi rispetto alla ricorsione.
Problema in due variabili
K(j, w)
= massimo valore ottenibile con uno zaino di capacità w
scegliendo gli item da 1
a j
.
Non possiamo più applicare la soluzione bruteforce, perchè abbiamo due variabili, j
e w
.
Allora, prendo l'elemento j
. Esso può essere o non essere nella soluzione: mi calcolo entrambe le alternative, e mi tengo l'alternativa dal valore più alto.
Se j
non è nella soluzione, il risultato diventerà K(j-1, w)
; se invece è nella soluzione, il risultato sarà j.valore + K(j-1, w-j.peso)
.
In pratica, prendiamo
K(j, w) = max
\begin{cases}
V_j + K(j-1, w-w_j)
K(j-1, w)
\end{cases}
Costruisco allora una matrice con j
su un asse e w
sull'altro.
Riempio le caselle con il valore di K(j, w)
.
Nella casella con K(j, w)
avremo la soluzione ottima.
Il tempo necessario per riempire tutte le caselle è nuovamente O(n * w)
, ancora pseudopolinomiale.
Per sapere che oggetti ho messo o no devo tenere traccia in qualche modo della catena del calcolo, usando, ad esempio, una pila.