8.6. La mochila cero-uno (Zero-One Knapsack).

En el problema de la mochila buscamos la forma en que podemos guardar objetos dentro de una mochila, con las restricciones de que necesitamos tener en la mochila los objetos de mayor costo (valor) posible y que el peso total no debe sobrepasar a un límite dado. Contamos con el costo y el peso de cada objeto, y disponemos de una cantidad ilimitada de ellos.

Este problema de optimización es NP-difícil, y puede ser replanteado como problema de decisión NP-completo de la siguiente forma: “podemos tener un costo de por lo menos c sin exceder el peso w?”.

Existe una versión más simple del problema, donde en lugar de tener una infinidad de elementos, sólo tenemos uno de cada tipo por lo que la decisión se reduce a si tomarlo o no. A este problema se le conoce como la mochila cero-uno.

Para resolver el problema de la mochila cero-uno, podemos considerar una función C, la que obtiene el máximo valor que se puede obtener teniendo un peso menor o igual j y utilizando hasta i objetos. Esta función la podemos obtener considerando los siguientes casos:
Problema ejemplo


SuperVenta

Problema
Hay promoción de SuperVenta en el SuperHiperMercado. Cada persona puede llevar solamente una unidad de cada mercancía (por ejemplo, una televisión o una zanahoria), pero a un precio muy bajo. Vamos a ir en familia al SuperHiperMercado. Cada persona tratará de comprar tanta mercancía como pueda en la SuperVenta. Hemos obtenido una lista con el precio y el peso de cada mercancía. También conocemos cual es el peso máximo que puede cargar cada persona. ¿Cuál es valor máximo de mercancías que se pueden comprar en la SuperVenta?

Entrada
La entrada consiste en t casos. La cantidad de casos (1 ≤ t ≤ 1000) está dado en la primera línea de la entrada.

Cada caso comienza con una línea que contiene a un solo entero n que indica la cantidad de objetos (1 ≤ n ≤ 1000). Después le siguen n líneas, cada una con dos enteros: p y w. El primer entero (1 ≤ p ≤ 100) corresponde al precio del objeto. El segundo entero (1 ≤ w ≤ 30) corresponde al peso del objeto. La siguiente línea contiene un entero (1 ≤ g ≤ 100) que representa la cantidad de personas en la familia. Las g líneas que le siguen contienen el peso máximo (1 ≤ mw ≤ 30) que el i-ésimo miembro de la familia puede cargar (1 ≤ ig).

Salida
Para cada caso prueba, tu programa debe calcular un entero. Imprime el valor máximo de las mercancías que puedes comprar con esa familia.

Solución: Para cada caso, leemos los pesos y los costos, y utilizamos los casos anteriores para obtener los valores máximos de la mochila. Algo importante es no calcular la mochila para cada persona en g, sino sólo calcular para el valor mayor de mw ya que los demás valores son subproblemas de éste y los podemos obtener al mismo tiempo.


Código


En las primeras cuatros líneas declaramos las variables que vamos a utilizar. Para los ciclos utilizamos i y k, las variables g, n, t, mw, p, w, las utilizamos de acuerdo a las especificaciones del problema, sólo que las dos últimas las hacemos arreglos. En la matriz c guardamos los resultados intermedios de la mochila, y el resultado total lo guardamos en r.

La función max (línea 6) nos devuelve el menor de los dos parámetros. La implementación es la misma que en problemas anteriores.

En la función knapsack (líneas 8 a 18) calculamos el valor máximo que podemos guardar en la mochila. Las variables i y j, las utilizamos para los ciclos. Comenzamos limpiando la matriz c (línea 11). Para cada objeto de la mochila (línea 12), vamos calculando el precio máximo que podemos obtener con un peso máximo de j (líneas 13 a 16). Al final regresamos el precio máximo que podemos obtener utilizando todos los objetos (línea 17).

Entre las líneas 20 y 37 tenemos el bloque principal del programa. Empezamos leyendo la cantidad de casos a procesar (línea 21). Para cada caso, leemos el número de objetos (línea 24), junto con el precio y peso de estos (líneas 25 y 26). Después inicializamos el resultado y calculamos la mochila con el valor máximo (30). Leemos la cantidad de personas (línea 29) y para cada una de ellas, leemos el máximo que pueden cargar (línea 32) e incrementamos el resultado con el valor máximo que pueden comprar (línea 33). Terminamos escribiendo el resultado (línea 35).


Caso ejemplo

Entrada: 2
5
52 1
79 6
13 12
76 11
60 4
3
7
11
4
3
65 2
50 10
59 8
4
12
5
2
5
Desarrollo: C[]                     w
  52  52  52  52  52  52  52  52  52  52  52  52
  52  52  52  52  52  79 131 131 131 131 131 131
n 52  52  52  52  52  79 131 131 131 131 131 131
  52  52  52  52  52  79 131 131 131 131 131 131
  52  52  52  60 112 112 131 131 131 139 191 191
c[5,7] = 131; r= 131;
c[5,11]= 191; r= 322;
c[5,4] =  60; r= 382;

C[]                     w
   0  65  65  65  65  65  65  65  65  65  65  65
n  0  65  65  65  65  65  65  65  65  65  65 115
   0  65  65  65  65  65  65  65  65 124 124 124
c[5,12]= 124; r= 124;
c[5,5] =  65; r= 189;
c[5,2] =  65; r= 254;
c[5,5] =  65; r= 319;
Salida: 382
319


Posibles Cambios

El tiempo de ejecución de este algoritmo es de O(nW), donde n es la cantidad de objetos y W el peso máximo. Esta solución es pseudo-polinomial, ya que, aunque parece polinomial, en realidad W no está en función del tamaño de la entrada.

Podemos mejorar algunos aspectos del algoritmo. Cuando el peso máximo es grande, es mejor utilizar una estrategia “arriba hacia abajo” para evitar cálculos innecesarios.

También es posible reducir el tamaño de matriz observando que sólo estamos utilizando el renglón actual y el anterior, y que las casillas que estamos modificando en el renglón actual con respecto al anterior son van del peso actual al peso máximo. Con esto podemos reducir fácilmente la matriz a dos renglones, y con algunos cambios, a un renglón.


Código en C



Tiempo de ejecución

Lenguaje Fecha Tiempos [s] Memoria [Kb]
Ejecución Mejor Mediana Peor
Pascal 5-Jun-2006 0.119 0.037 0.219 14.710 448
C 10-Jun-2006 0.105 396


Otros problemas

Valladolid:





© Pier Paolo Guillen Hernandez
World of πer