Anda di halaman 1dari 3

Algoritmo da Mochila booleana (0/1) (não pegar/pegar)

O problema da mochila 0/1 pode ser a princípio resolvido por 2 algoritmos (recursivo ou programação dinâmica)

Em termos de performance o algoritmo recursivo é muito ineficiente, “pendurando” na resolução de vários problemas
não muito complexos. O algoritmo que utiliza programação dinâmica pode não ser lá tão eficiente, mas é infinitamente
mais eficiente que o algoritmo recursivo (mochila recursiva)

O algoritmo de programação dinâmica [CLR 17.2-1] consome O(nW) unidades de tempo. Mas isso só é possível se W,
w[1],…,w[n] forem todos inteiros não-negativos.

A execução do algoritmo usando PD (programação dinâmica) se faz através da recurção em iteração com apoio de uma
tabela. Na tabela t[m][n] são guardadas todas as soluções em instância possíveis.

Abaixo segue o algoritmo, onde:

Variável Descrição
int w[] Vetor contendo os pesos (weight) de cada um dos itens
int v[] Vetor contendo os valores (values) de cada um dos itens
int n Número de itens que serão trabalhados
int W Peso total (capacidade da mochila)

int mochila (int w[], int v[], int n, int W){


for (int y=0; y <=W; y++) {
t[0][y]=0;
for (int i=1; i <= n;i++) {
a = t[i-1][y];
if (w[i] > y){
b=0;
} else { No programa principal, sua chamada seria
b=t[i-1][y-w[i]] + v[i];
} mochila(w,v,objetos,carga);
if (a>b)
t[i][y] = a;
else
t[i][y] = b;
}
}
return(t[n][W]);
}

Um exemplo clássico da aplicabilidade deste algoritmo é o que lhe dá o nome (capítulo 16 - Th.H. Cormen, Ch.E.
Leiserson, R.L. Rivest, C. Stein ), onde um ladrão de obras de artes entra em uma galeria com uma maleta de tamanho
x. Obviamente ele quer carregar o máximo de valor em obras de artes que caibam em sua maleta. O algoritmo da
mochila é usado para resolver este problema. Segue uma ilustração.
Tem-se um mochila com capacidade para carregar 5 quilos e um total de 4 itens, cujos pesos e valores são os seguintes:

Peso Valor Obviamente, segundo estes itens ao lado esquerdo, a melhor combinação a carregar seria
4 500 o segundo e o quarto item (R4 400,00 + R$ 450,00) totalizando R$ 850,00.
2 400
1 300 Note que pelo peso, poderia-se pegar o item de valor R$ 500,00 + R$ 300,00 (5 kg)
3 450 ou R$ 450,00 +R$ 300,00 (4 kg). Porém, nenhuma destas é a melhor escolha.

Suponha que W = 5 e n = 4. Os vetores w e v são dados pela figura esquerda. A figura direita dá a tabela t.
1 2 3 4 0 1 2 3 4 5
w 4 2 1 3 0 0 0 0 0 0 0
v 500 400 300 450 1 0 0 0 0 500 500
2 0 0 400 400 500 500
3 0 300 400 700 700 800
4 0 300 400 700 750 850
O número de linhas será sempre a quantidade de itens (n) e o número de colunas será sempre o W, que seria o tamanho
da mochila. Note que o maior valor possível sempre estará na posição t[n][W] (última linha e coluna à direita).

Em muitos problemas, além de saber quanto é possível carregar na mochila, necessita-se saber quais valores serão
selecionados pelo algoritmo. Isso é muito simples de se obter através da tabela t (a mesma tabela do exemplo anterior).
O procedimento para isso é listado abaixo:
a) define-se um vetor x[maxobjetos] que conterá 1 ou 0 para cada posição indicando, respectivamente, se o valor
correspondente do vetor w deve ser selecionado ou não. Neste caso é x[5] e se ocupa as posições de 1 a 4.
b) partindo-se do elemento inferior direito (último da matriz t) diminui-se o valor selecionado (w[i]) da coluna, caso ele
seja diferente do elemento da linha anterior. Neste caso, 850 é diferente de 800, portanto coloca-se o valor 1 em x[4] e
diminui-se 3 (w[i]) do y.
c) repete-se o procedimento enquanto a linha for maior do que 0
Volta 2 colunas (0)
1 2 3 4 t 0 1 2 3 4 5
w 4 2 1 3 0 0 0 0 0 0 0
v 500 400 300 450 1 0 0 0 0 500 500
2 0 0 400 400 500 500
3 0 300 400 700 700 800
4 0 300 400 700 750 850 Volta 3 colunas (2)

Segue o código desta parte:


Nota: Isso só é necessário quando se precisa saber quais valores foram selecionados.
int y=W;
for ( int i=n; i >0; i--){
if (t[i][y]==t[i-1][y]) {
x[i]=0;
} else {
x[i]=1;
y=y-w[i];
}
}
0 1 2 3 4
X 0 0 1 0 1

Por último, pode-se desejar somar o total em peso colocado na mochila. Isso se deve ao fato de que mesmo que a
mochila tenha capacidade para 5 kg, pode ser que devido aos objetos, seja colocado na mesma menos do que isso
(4,3,2,...etc).
int peso=0;
for (int i = 1; i<=n; i++) {
if (x[i]==1) {
peso += w[i]; // 2 + 3 = 5
}
}

Agora vamos aplicar em um problema prático bem simples:


Um grande supermercado fez uma promoção, onde uma pessoa pode entrar e carregar tudo o que conseguir para pegar o
maior valor possível. São 1000 produtos (no máximo) e a pessoa pode carregar um peso que é no máximo 30 kg (varia
de pessoa para pessoa) de produtos não repetidos e cada produto pode valer de 1 a R$ 100,00.
Entrada
4 4 produtos
72 17 R$ 72,00 e 17 kg
44 23 R$ 44,00 e 23 kg
31 24 R$ 31,00 e 24 kg
22 2 R$ 22,00 e 2 kg
26 A pessoa pode carregar 26 kg
Saída
98 A pessoa pegou o produto de 17 kg e o produto de 2 kg

PS.: Note que neste caso específico não precisamos saber quais os produtos foram pegos, ou seja, não há necessidade
de utilizar a segunda parte do algoritmo.
#include <iostream>

#define MAXOBJ 1001 // até 1000 produtos


#define MAXCARGA 31 // carga máxima de 30 kg

using namespace std;

int a,b,t[MAXOBJ][MAXCARGA],x[MAXOBJ];

int mochila (int w[], int v[], int n, int W){


for (int y=0; y <=W; y++) {
t[0][y]=0;
for (int i=1; i <= n;i++) {
a = t[i-1][y];
if (w[i] > y){
b=0;
} else {
b=t[i-1][y-w[i]] + v[i];
}
if (a>b)
t[i][y] = a;
else
t[i][y] = b;
}
}
return(t[n][W]);
}

int main(){
int w[MAXOBJ],v[MAXOBJ];
int objetos,carga,total;
cin >> objetos;
for (int i=1; i<=objetos; i++){
cin >> v[i];
cin >> w[i];
}
cin >> carga;
total = mochila(w,v,objetos,carga);
cout << total << endl;
return(0);
}

Resolver (por ordem de dificuldade)


- UVA 10130 (Supersale) Considero nível 1
- UVA 624 (CD) // Várias combinações são possíveis, eu perdi tempo tentando a mesma saída do uva toolkit! Nível 2
- UVA 562 (Dividing coins) Nível 2
- UVA 10819 ( Trouble of 13 Dots) // knapsack um pouco modificado! Desafio vocês a acertarem este!!! Nível 3