15.05.2013 Views

Ejercicios resueltos

Ejercicios resueltos

Ejercicios resueltos

SHOW MORE
SHOW LESS

Create successful ePaper yourself

Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.

Ejercicio 1. Programación dinámica<br />

[ITIS/ITIG, Feb 2007]<br />

Dada una cantidad de dinero C, deseamos encontrar el mínimo número de<br />

monedas que cubra exactamente, si es posible, esta cantidad. Para ello<br />

conocemos los valores de los N tipos de monedas y el número de monedas<br />

disponibles de cada tipo. Diseña un algoritmo dinámico que resuelva el<br />

problema detallando lo siguiente:<br />

La estructura de cálculos intermedios. (0,5 puntos)<br />

La relación recursiva. (1 punto)<br />

La función o procedimiento que implemente este algoritmo. (2,5<br />

puntos).<br />

Yolanda García, Jesús Correas (DSIC - UCM) 1 / 32


Solución Ejercicio 1. Programación dinámica<br />

Este ejercicio es muy parecido al que hemos visto anteriormente de<br />

devolución del cambio<br />

La diferencia es que el número de monedas disponibles de cada tipo<br />

es limitado<br />

Recordando el caso de estudio de devolución del cambio, la expresión<br />

recurrente que lo definía era:<br />

C[i, j] = mín(C[i − 1, j], 1 + C[i, j − di])<br />

De los dos accesos recurrentes de la expresión anterior:<br />

a) C[i − 1, j] corresponde con la decisión de no tomar ninguna moneda de<br />

valor di. Este caso no modifica el número de monedas utilizado para<br />

obtener C[i, j]. No afecta la limitación en el número de monedas.<br />

b) 1 + C[i, j − di] corresponde con la selección de una moneda de valor di<br />

(al menos). Este caso sí modifica el número de monedas que se<br />

necesita para obtener C[i, j] en función de C[i, j − di], pues se utiliza<br />

una moneda de valor di.<br />

Yolanda García, Jesús Correas (DSIC - UCM) 2 / 32


Solución Ejercicio 1. Programación dinámica (cont.)<br />

Una posible solución es considerar tantos casos como monedas de<br />

valor di estén disponibles, y después calcular el mínimo de todos ellos<br />

C[i, j] = mín (k + C[i − 1, j − di ∗ k])<br />

0≤k≤mi<br />

donde mi es el número de monedas disponibles de valor di.<br />

La recurrencia<br />

⎧<br />

completa es:<br />

0 si j = 0<br />

+∞ si j < 0<br />

+∞ si i = 1<br />

⎪⎨<br />

y (j < di o j > di ∗ mi<br />

C(i, j) =<br />

o j mod di = 0)<br />

⌊j/di⌋ si i = 1 y j mod di = 0<br />

y j ≤ di ∗ mi<br />

⎪⎩ mín (k + C[i − 1, j − di ∗ k]) en otro caso<br />

0≤k≤mi<br />

Para cada uno de los valores de k (desde 1 hasta mi) se considera<br />

tomar las k monedas de valor di, en lugar de hacerlo de una en una<br />

como en la versión original del problema de devolución del cambio<br />

Yolanda García, Jesús Correas (DSIC - UCM) 3 / 32


Solución Ejercicio 1. Programación dinámica (cont.)<br />

fun monedas2(d[1..N], m[1..N],Cant)<br />

crear C[1..N,0..Cant]<br />

desde i← 1 hasta N hacer C[i,0] ← 0<br />

desde i← 1 hasta N hacer<br />

desde j← 1 hasta Cant hacer<br />

si i=1 Y j k+C[i-1,j-k*d[i]] entonces min ← k+C[i-1,j-k*d[i]]<br />

fin desde<br />

C[i,j] ← min<br />

fin si<br />

fin desde<br />

fin desde<br />

devolver C[N,Cant]<br />

fin fun<br />

Yolanda García, Jesús Correas (DSIC - UCM) 4 / 32


Ejercicio 2. Programación dinámica<br />

[ITIS/ITIG, Sep 2007] En el departamento de una empresa de traducciones<br />

se desea hacer traducciones de textos entre varios idiomas. Se dispone de<br />

algunos diccionarios. Cada diccionario permite la traducción (bidireccional)<br />

entre dos idiomas. En el caso más general, no se dispone de diccionarios<br />

para cada par de idiomas por lo que es preciso realizar varias traducciones.<br />

Dados N idiomas y M diccionarios, determina si es posible realizar la<br />

traducción entre dos idiomas dados y, en caso de ser posible, determina la<br />

cadena de traducciones de longitud mínima.<br />

Diseña un algoritmo dinámico que resuelva el problema detallando lo<br />

siguiente:<br />

1. La estructura de cálculos intermedios utilizada (0,5 puntos).<br />

2. La relación recursiva entre subproblemas (0,5 puntos).<br />

3. El procedimiento o función que implemente el algoritmo (2 puntos).<br />

Yolanda García, Jesús Correas (DSIC - UCM) 5 / 32


Ejercicio 2. Programación dinámica (cont.)<br />

Ejemplo 1:<br />

Traducir del latín al arameo disponiendo de los siguientes diccionarios:<br />

latín-griego, griego-etrusco, griego-demótico, demótico-arameo<br />

Solución: sí es posible la traducción: latín-griego-demótico-arameo,<br />

realizando tres traducciones.<br />

Ejemplo 2:<br />

Traducir del latín al arameo disponiendo de los siguientes diccionarios:<br />

latín-griego, arameo-etrusco, griego-demótico, demótico-hebreo<br />

Solución: No es posible la traducción.<br />

Yolanda García, Jesús Correas (DSIC - UCM) 6 / 32


Solución ejercicio 2. Programación dinámica<br />

Una forma sencilla de representar este problema es considerando las<br />

secuencias de traducciones como “caminos” de un idioma a otro<br />

De esta forma, podemos considerar los idiomas como los nodos de un<br />

grafo, y los diccionarios como las aristas<br />

Es importante tener en cuenta lo que se pide como resultado del<br />

algoritmo:<br />

a) saber si es posible la traducción entre dos idiomas dados<br />

b) y la cadena de traducciones de longitud mínima<br />

Vemos que es un problema de optimización (cadena de traducciones<br />

de longitud mínima), y conocemos un algoritmo para calcular los<br />

caminos mínimos entre cualquier par de nodos de un grafo: el<br />

algoritmo de Floyd<br />

El único dato que nos falta para aplicarlo directamente es la longitud<br />

y orientación de las aristas<br />

Yolanda García, Jesús Correas (DSIC - UCM) 7 / 32


Solución ejercicio 2. Programación dinámica (cont.)<br />

Si sólo nos pidieran saber si es posible la traducción entre dos<br />

idiomas, podríamos utilizar una versión simplificada del algoritmo de<br />

Floyd: el algoritmo de Warshall, que utiliza una matriz de adyacencia<br />

booleana para saber si dos nodos están conectados o no (en lugar de<br />

una matriz de distancias), [GV00], p. 197<br />

Como nos piden la cadena mínima, debemos asignar una distancia en<br />

cada arista, por ejemplo 1<br />

También se dice que los diccionarios permiten traducciones<br />

bidireccionales: por cada diccionario habrá dos aristas, cada una en un<br />

sentido (la matriz de adyacencia será simétrica)<br />

Respecto a las estructuras de datos intermedias:<br />

◮ En el algoritmo de Floyd vimos que la estructura de datos intermedia<br />

coincide con la matriz resultado, y es una matriz cuadrada N × N<br />

◮ También se nos pide la cadena de traducciones por las que tenemos<br />

que pasar: necesitamos una matriz para almacenar los nodos<br />

intermedios por los que pasan los caminos mínimos<br />

Yolanda García, Jesús Correas (DSIC - UCM) 8 / 32


Solución ejercicio 2. Programación dinámica (cont.)<br />

fun trad(L[1..n,1..n],i,j,c[1..n])<br />

crear D[1..n,1..n],P[1..n,1..n]<br />

floyd(L,D,P)<br />

si D[i,j] = +∞ entonces<br />

k ← 1<br />

cam(P,i,j,c,k)<br />

c[k] ← -1<br />

fin si<br />

devolver D[i,j]<br />

fin fun<br />

proc cam(P[1..n,1..n],i,j,c[1..n],k)<br />

si P[i,j] = 0 entonces<br />

cam(P,i,P[i,j],c,k)<br />

c[k] ← P[i,j]<br />

k ← k+1<br />

cam(P,P[i,j],j,c,k)<br />

fin si<br />

fin proc<br />

proc floyd(L[1..n,1..n],D[1..n,1..n],P[1..n,1..n])<br />

desde i ← 1 hasta n hacer<br />

desde j ← 1 hasta n hacer<br />

P[i,j] ← 0<br />

fin desde<br />

fin desde<br />

D ← L<br />

desde k ← 1 hasta n hacer<br />

desde i ← 1 hasta n hacer<br />

desde j ← 1 hasta n hacer<br />

si D[i,k]+D[k,j] < D[i,j] entonces<br />

D[i,j] ← D[i,k]+D[k,j] ; P[i,j] ← k<br />

fin si<br />

fin desde<br />

fin desde<br />

fin desde<br />

fin proc<br />

Yolanda García, Jesús Correas (DSIC - UCM) 9 / 32


Ejercicio 3. Programación dinámica<br />

[ITIS/ITIG, Jun 2007] Un excéntrico nutricionista va a un restaurante. En<br />

la carta aparecen todos los platos disponibles con el número de calorías. El<br />

nutricionista conoce el número mínimo de calorías que su cuerpo necesita<br />

en esa comida. Su objetivo es encontrar el menú que cubra exactamente<br />

esa cantidad de calorías o las supere de forma mínima. Además, no quiere<br />

repetir platos.<br />

Diseña un algoritmo dinámico que determine qué platos forman parte del<br />

menú óptimo y el número de calorías del menú óptimo. Detalla lo<br />

siguiente:<br />

1. Las estructuras y/o variables necesarias para representar la<br />

información del problema (0,5 puntos).<br />

2. La estructura de cálculos intermedios utilizada (1 punto).<br />

3. La relación recursiva entre subproblemas (1 punto).<br />

4 y 5. El procedimiento o función que implementa el algoritmo (2,5 puntos).<br />

Yolanda García, Jesús Correas (DSIC - UCM) 10 / 32


Solución ejercicio 3. Programación dinámica<br />

Este problema es de optimización (menú con mínimo número de<br />

calorías por encima de una cantidad)<br />

Entre los problemas que ya hemos visto, el más parecido es el de la<br />

mochila 0-1, pues los platos de la carta se eligen completos o no se<br />

eligen<br />

Las diferencias con este problema son: a) cómo se calcula el óptimo<br />

(mínimo en lugar de máximo), y b) la existencia de un valor de<br />

referencia (número mínimo de calorías necesario)<br />

Estas diferencias pueden resolverse utilizando una expresión<br />

recurrente apropiada para llenar la tabla<br />

Para conocer las estructuras necesarias para representar el problema<br />

es necesario analizar lo que se nos proporciona y se nos pide:<br />

◮ Se nos proporciona el número de calorías de cada plato: un vector<br />

C[1..n], donde n es el número de platos de la carta<br />

◮ Se nos pide el menú elegido: un vector M[1..n] de los platos elegidos (el<br />

tamaño del vector es el número máximo de platos que podemos elegir)<br />

◮ También se nos pide el número de calorías de este menú<br />

Yolanda García, Jesús Correas (DSIC - UCM) 11 / 32


Solución ejercicio 3. Programación dinámica (cont.)<br />

La estructura de datos intermedia puede ser, como en el problema de<br />

la mochila, una matriz D[1..n, 0..Cal] con una fila por cada plato de la<br />

carta (desde 1 hasta n), y el número total de calorías en las columnas<br />

D[i, j] contiene el número de calorías del menú óptimo considerando<br />

los i primeros platos de la carta: aquél con el menor número de<br />

calorías (mayor o igual a j)<br />

El valor que buscamos es el correspondiente a D[n, Cal]<br />

Para calcular D[i, j], se pueden dar dos casos:<br />

◮ Si no se elige el plato i de la carta, entonces el menú elegido para<br />

D[i, j] contendrá exclusivamente platos de 1 a i − 1: D[i − 1, j]<br />

◮ Si se elige el plato i, entonces el número de calorías total del<br />

menú D[i, j] es ci + D[i − 1, j − ci]<br />

Yolanda García, Jesús Correas (DSIC - UCM) 12 / 32


Solución ejercicio 3. Programación dinámica (cont.)<br />

Hay que tener en cuenta algunos casos especiales:<br />

◮ D[1, j] = +∞, j > c1, porque no es posible conseguir un menú de más<br />

de c1 calorías utilizando exclusivamente el plato 1 (no se puede repetir<br />

el mismo plato)<br />

◮ D[1, j] = c1, 0 < j ≤ c1<br />

◮ D[i, j] = 0, j ≤ 0, pues el menú óptimo con al menos 0 (o menos)<br />

calorías consiste en no elegir ningún plato<br />

Por tanto, la expresión recurrente es la siguiente:<br />

⎧<br />

⎪⎨<br />

0 si j ≤ 0<br />

+∞ si i = 1 y j > ci<br />

D[i, j] =<br />

c1 ⎪⎩<br />

si i = 1 y 0 < j ≤ ci<br />

mín(C[i − 1, j], ci + C[i − 1, j − ci]) en otro caso<br />

la función mín() permite elegir el menú de menos calorías<br />

Los valores de la tabla con valor +∞ garantizan que no se va a elegir<br />

un menú que tenga menos calorías que el mínimo necesario<br />

Yolanda García, Jesús Correas (DSIC - UCM) 13 / 32


Solución ejercicio 3. Programación dinámica (cont.)<br />

fun menu(c[1..n],Cal,m[1..n])<br />

crear D[1..n,0..Cal]<br />

desde pl ← 1 hasta n hacer<br />

D[pl,0] ← 0<br />

desde ncal ← 1 hasta Cal hacer<br />

si pl=1 Y ncal > c[1] entonces D[pl,ncal] ← +∞<br />

si no si pl=1 entonces D[pl,ncal] ← c[1]<br />

si no si ncal < c[pl] entonces<br />

D[pl,ncal] ← mín(D[pl-1,ncal],c[pl])<br />

si no<br />

D[pl,ncal] ← mín(D[pl-1,ncal],c[pl]+D[pl-1,ncal-c[pl]])<br />

fin si<br />

fin desde<br />

fin desde<br />

si D[n,Cal] < +∞ entonces obtener menu(D,c,m)<br />

devolver D[n,Cal]<br />

fin fun<br />

Yolanda García, Jesús Correas (DSIC - UCM) 14 / 32


Solución ejercicio 3. Programación dinámica (cont.)<br />

proc obtener menu(D[1..n,0..Cal],c[1..n],m[1..n])<br />

plato ← 1<br />

i ← n<br />

j ← Cal<br />

mientras i > 1 Y j > 0 hacer<br />

si D[i,j]=D[i-1,j] entonces<br />

i ← i-1<br />

si no<br />

m[plato] ← i<br />

plato ← plato + 1<br />

j ← j-c[i]<br />

i ← i-1<br />

fin si<br />

fin mientras<br />

si i = 1 ∧ j > 0 entonces m[plato] ← 1 ; plato ← plato+1<br />

desde i ← plato hasta n hacer m[i] ← -1<br />

fin proc<br />

Yolanda García, Jesús Correas (DSIC - UCM) 15 / 32


Ejercicio 4. Programación dinámica<br />

[GV00] Supongamos que existen n bancos en los que podemos invertir, y<br />

disponemos de una cantidad Cant para invertirla. Cada banco nos<br />

proporciona intereses según una función monótona creciente, fi(x), donde<br />

x es el importe a invertir e i el banco en el que se invierte.<br />

Diseñad un algoritmo dinámico que encuentre la inversión óptima: la<br />

cantidad que se debe invertir en cada banco para maximizar los intereses<br />

obtenidos.<br />

Yolanda García, Jesús Correas (DSIC - UCM) 16 / 32


Solución ejercicio 4. Programación dinámica<br />

Este problema es muy parecido al anterior, aunque en este caso lo que<br />

se debe hacer es maximizar la expresión<br />

f1(x1) + f2(x2) + ... + fn(xn)<br />

con la restricción: x1 + x2 + ... + xn = Cant<br />

Como en el caso anterior, vamos a intentar encontrar una expresión<br />

recurrente respecto al número de bancos considerados<br />

Representamos con I (i, j) al interés máximo al invertir la cantidad j<br />

en los primeros i bancos:<br />

I (i, j) = f1(x1) + f2(x2) + ... + fi(xi)<br />

con la restricción x1 + x2 + ... + xi = j.<br />

Se verifica el principio del óptimo: si I (i, j) es el interés máximo para i<br />

bancos, entonces I (i − 1, j − xi) es el interés máximo para i − 1<br />

bancos (se verifica para cualquier número de bancos menor a i)<br />

Por tanto, la expresión recurrente resultante es:<br />

f1(j) si i = 1<br />

I (i, j) = máx<br />

0≤t≤j (I (i − 1, j − t) + fi(t)) en otro caso<br />

Yolanda García, Jesús Correas (DSIC - UCM) 17 / 32


Solución ejercicio 4. Programación dinámica (cont.)<br />

Podemos considerar que las funciones fi(x) se proporcionan como<br />

valores de una matriz F [1..n, 0..Cant], donde F [i, j] contiene el<br />

importe de los intereses que proporciona el banco i al invertir la<br />

cantidad j<br />

Como estructura de datos intermedia, utilizamos una matriz<br />

I [1..n, 0..Cant]<br />

Dada la estructura de la recurrencia, se puede generar la matriz I por<br />

filas<br />

Yolanda García, Jesús Correas (DSIC - UCM) 18 / 32


Solución ejercicio 4. Programación dinámica (cont.)<br />

fun intereses(F[1..n,0..Cant],Cant)<br />

crear I[1..n,0..Cant]<br />

desde i ← 1 hasta n hacer<br />

I[i,0] ← 0<br />

fin desde<br />

desde j ← 1 hasta Cant hacer<br />

I[1,j] ← F[1,j]<br />

fin desde<br />

desde i ← 2 hasta n hacer<br />

desde j ← 1 hasta Cant hacer<br />

I[i,j] ← F[i,j]<br />

desde t ← 0 hasta j-1 hacer<br />

si I[i,j] < I[i-1,j-t]+F[i,t] entonces<br />

I[i,j] ← I[i-1,j-t]+F[i,t]<br />

fin si<br />

fin desde<br />

fin desde<br />

fin desde<br />

devolver I[n,Cant]<br />

fin fun<br />

Falta la obtención de<br />

los importes invertidos<br />

en cada uno de los<br />

bancos<br />

¿Cómo podría incluirse<br />

en este algoritmo?<br />

Yolanda García, Jesús Correas (DSIC - UCM) 19 / 32


Ejercicio 5. Programación dinámica<br />

[GV00] Subsecuencia común máxima. Dada una secuencia<br />

X = {x1 x2 ... xm}, se dice que Z = {z1 z2 ... zk} (k ≤ m) es una<br />

subsecuencia de X si existe una secuencia creciente de índices de X<br />

{i1 i2 ... ik} tales que para todo j = 1, 2...k, xij = zj.<br />

Dadas dos secuencias X e Y , Z es una subsecuencia común de X e Y si<br />

es subsecuencia de X y subsecuencia de Y . Determinar utilizando<br />

programación dinámica la subsecuencia de longitud máxima común a X e<br />

Y .<br />

Ejemplo 1 : dadas X = {A, B, C, B, D, A, B} e Y = {B, D, C, A, B, A}, la<br />

secuencia {B, C, A} es una subsecuencia comun de ambas. Las secuencias<br />

{B, C, B, A} y {B, D, A, B} son subsecuencias comunes de máxima<br />

longitud.<br />

1 http://webdiis.unizar.es/˜kftricas/Asignaturas/ea/<strong>Ejercicios</strong>/4-<br />

EjProgramacionDinamica.pdf<br />

Yolanda García, Jesús Correas (DSIC - UCM) 20 / 32


Solución ejercicio 5. Programación dinámica<br />

Las secuencias de entrada son X = {x1 x2 ... xm} e Y = {y1 y2 ... yn}<br />

Llamaremos L(i, j) a la longitud de la secuencia común máxima de Xi<br />

e Yj, siendo éstas los prefijos de X e Y de longitudes i y j,<br />

respectivamente:<br />

Xi = {x1 x2 ... xi}<br />

Yj = {y1 y2 ... yj}<br />

¿Se cumple el principio de optimalidad?<br />

Yolanda García, Jesús Correas (DSIC - UCM) 21 / 32


Solución ejercicio 5. Programación dinámica<br />

Las secuencias de entrada son X = {x1 x2 ... xm} e Y = {y1 y2 ... yn}<br />

Llamaremos L(i, j) a la longitud de la secuencia común máxima de Xi<br />

e Yj, siendo éstas los prefijos de X e Y de longitudes i y j,<br />

respectivamente:<br />

Xi = {x1 x2 ... xi}<br />

Yj = {y1 y2 ... yj}<br />

¿Se cumple el principio de optimalidad?<br />

En cada paso del algoritmo, podemos decidir si el último carácter de<br />

Xi e Yj forma parte de la secuencia común máxima:<br />

◮ Si el último carácter de ambas secuencias es igual, entonces se añade a<br />

la secuencia común máxima: en este caso, L(i, j) tiene el siguiente<br />

valor: L(i − 1, j − 1) + 1<br />

◮ Si el último carácter de Xi e Yj no es igual, entonces la secuencia<br />

común máxima no lo contiene, y el valor de L(i, j) será el mayor de<br />

L(i, j − 1) y L(i − 1, j)<br />

olanda García, Jesús Correas (DSIC - UCM) 21 / 32


Solución ejercicio 5. Programación dinámica (cont.)<br />

Por tanto, la expresión recurrente es:<br />

⎧<br />

⎨ 0 si i = 0 o j = 0<br />

L(i, j) = L(i − 1, j − 1) + 1 si i = 0, j = 0, xi = yj<br />

⎩<br />

máx(L(i, j − 1), L(i − 1, j)) si i = 0, j = 0, xi = yj<br />

¿Qué elemento de la tabla de cálculos intermedios contiene la<br />

longitud de la subsecuencia común máxima?<br />

¿Cómo sería la implementación de esta recurrencia?<br />

¿Cómo se puede obtener la subsecuencia común máxima?<br />

Yolanda García, Jesús Correas (DSIC - UCM) 22 / 32


Ejercicio 6. Programación dinámica<br />

[ITIS/ITIG, Feb 2006] Nicanor Cienfuegos quiere impresionar a su novia.<br />

Ha decidido gastarse todo su dinero en flores. Según los criterios estéticos<br />

de Nicanor el ramo ideal es aquel que minimiza el número de flores. Dado<br />

el prestigio de la floristería, piensa que para cada tipo de flor pueden<br />

vender un número infinito de copias. Ante la cara de asombro de su novia<br />

Nicanor recapacita, quizás no fue correcta su suposición. Mediante<br />

programación dinámica, diseñad dos algoritmos que resuelvan el problema<br />

detallando lo siguiente:<br />

Suposición 1: número infinito de copias (2 puntos):<br />

◮ Estructura de datos intermedia.<br />

◮ Relación recursiva entre casos y subcasos.<br />

◮ Implementación del código.<br />

Suposición 2: número finito de copias (2 puntos):<br />

◮ Estructura de datos intermedia.<br />

◮ Relación recursiva entre casos y subcasos.<br />

◮ Implementación del código.<br />

Yolanda García, Jesús Correas (DSIC - UCM) 23 / 32


Solución ejercicio 6. Programación dinámica<br />

Este problema es muy parecido al de la devolución del cambio: hay<br />

que minimizar el número de flores, sin sobrepasar otra medida (el<br />

precio total)<br />

La diferencia está en que en este caso puede darse la situación de que<br />

no se gaste exactamente el dinero disponible, sino algo menos<br />

Para que el gasto sea lo más próximo a la cantidad disponible,<br />

debemos considerarlas en orden creciente de precio. Denominamos pi<br />

al precio de la flor i, p1 ≤ p2 ≤ ... ≤ pn<br />

Así, el importe no gastado será inferior a p1<br />

Con C(i, j) se representa el número mínimo de flores de 1 a i que se<br />

pueden utilizar para gastar la cantidad j de dinero<br />

La expresión de la recurrencia es:<br />

⎧<br />

⎪⎨<br />

+∞ si j < 0<br />

0 si j = 0<br />

C(i, j) =<br />

⎪⎩<br />

⌊j/p1⌋ si i = 1 y j > 0<br />

mín(C(i − 1, j), 1 + C(i, j − pi)) en otro caso<br />

Yolanda García, Jesús Correas (DSIC - UCM) 24 / 32


Solución ejercicio 6. Programación dinámica (cont.)<br />

En la recurrencia anterior se ha considerado que se puede elegir<br />

cualquier número de flores de cada tipo (la expresión 1 + C(i, j − pi)<br />

permite volver a seleccionar el mismo tipo de flor indefinidamente)<br />

Si no hay un número ilimitado de flores, la recurrencia debe<br />

considerar el número máximo de flores disponible por cada tipo<br />

Denominamos fi al número de flores del tipo i<br />

La expresión<br />

⎧<br />

de la recurrencia pasa a ser la siguiente:<br />

+∞ si j < 0<br />

⎪⎨ 0 si j = 0<br />

C(i, j) = ⌊j/pi⌋ si i = 1 y j ≤ pifi<br />

fi<br />

si i = 1 y j > pifi (∗)<br />

⎪⎩ mín {k + C(i − 1, j − k ∗ pi)} en otro caso<br />

0≤k≤fi<br />

(*) Podemos considerar que compramos todas las flores de la tienda y<br />

además nos sobra dinero<br />

La especificación del algoritmo es similar a las vistas en casos<br />

anteriores<br />

Yolanda García, Jesús Correas (DSIC - UCM) 25 / 32


Ejercicio 7. Programación dinámica<br />

[ITIS/ITIG, feb 2008] A Nicanor Cienfuegos le han hecho un regalo. Como no le<br />

gusta ha decidido cambiarlo por otros productos. Su cambio ideal es el siguiente:<br />

el valor de los productos tiene que ser igual al valor del regalo o superarlo de<br />

forma mínima. No le importa tener varias copias del mismo producto. Suponiendo<br />

conocidos los productos de la tienda, sus precios y el número de unidades de cada<br />

producto:<br />

a) (3 puntos) Diseña un algoritmo dinámico que determine el valor de los<br />

productos elegidos en el canje, detallando<br />

◮ La estructura de cálculos intermedios,<br />

◮ La relación recursiva, y<br />

◮ La función o procedimiento que implemente este algoritmo.<br />

b) (1 punto) Proporciona una función con memoria que implemente este<br />

algoritmo.<br />

Yolanda García, Jesús Correas (DSIC - UCM) 26 / 32


Solución ejercicio 7. Programación dinámica<br />

Este ejercicio es una combinación de otros dos vistos en clase: cambio de<br />

moneda con un número limitado de monedas (feb 07), y el nutricionista (jun<br />

07).<br />

La suma de los valores de los productos debe ser igual o superior (de forma<br />

mínima) a la cantidad de dinero disponible<br />

Existe un número limitado de objetos de cada tipo en la tienda<br />

Estructura para cálculos intermedios una tabla C[1..N, 0..R], donde N es el<br />

número de tipos de objetos en la tienda, y R es el valor del regalo a cambiar<br />

La relación recursiva es la siguiente (pi es el precio y ui el número de<br />

unidades del objeto i):<br />

⎧<br />

0 si j ≤ 0<br />

⎪⎨ ⌈j/pi⌉ ∗ pi<br />

si i = 1 y j ≤ pi ∗ ui<br />

C(i, j) = +∞ si i = 1 y j > pi ∗ ui<br />

⎪⎩ mín (pi ∗ k + C[i − 1, j − pi ∗ k]) en otro caso<br />

0≤k≤Ki<br />

donde Ki = mín(ui, ⌈j/pi⌉).<br />

Yolanda García, Jesús Correas (DSIC - UCM) 27 / 32


Solución ejercicio 7. Programación dinámica (cont.)<br />

fun regalos(p[1..N], u[1..N],Cant)<br />

crear C[1..N,0..Cant]<br />

desde i← 1 hasta N hacer C[i,0] ← 0<br />

desde i← 1 hasta N hacer<br />

desde j← 1 hasta Cant hacer<br />

si i=1 entonces<br />

si j/p[i] ≤ u[i] entonces C[i,j]← ⌈j/p[i]⌉*p[i]<br />

si no C[i,j]← +∞<br />

si no<br />

min ← +∞<br />

desde k← 0 hasta mín(⌈j/p[i]⌉, u[i]) hacer<br />

si k*p[i]>j entonces min ← mín(min,k*p[i])<br />

si no min ← mín(min,k*p[i]+C[i-1,j-k*p[i]])<br />

fin desde<br />

C[i,j] ← min<br />

fin si<br />

fin desde<br />

fin desde<br />

devolver C[N,Cant]<br />

fin fun<br />

Yolanda García, Jesús Correas (DSIC - UCM) 28 / 32


Solución ejercicio 7. Programación dinámica (cont.)<br />

Una posible solución para el apartado b) es la siguiente:<br />

//C[1..N,1..Cant] es una tabla global con valor inicial -1<br />

fun regalos mem(p[1..N], u[1..N],i,j)<br />

si j≤0 entonces devolver 0<br />

si C[i,j]≥0 entonces devolver C[i,j]<br />

si i=1 entonces<br />

si j/p[1] ≤ u[1] entonces C[1,j]← ⌈j/p[1]⌉*p[1]<br />

si no C[i,j]← +∞<br />

si no<br />

min ← +∞<br />

desde k← 0 hasta mín(⌈j/p[i]⌉, u[i]) hacer<br />

aux ← k*p[i]+regalos mem(p,u,i-1,j-k*p[i])<br />

si min > aux entonces min ← aux<br />

fin desde<br />

C[i,j] ← min<br />

fin si<br />

devolver C[i,j]<br />

fin fun<br />

Yolanda García, Jesús Correas (DSIC - UCM) 29 / 32


Ejercicio 8. Programación dinámica<br />

[ITIS/ITIG, Jun 2008] Una Organización No Gubernamental dispone de un<br />

fondo para la financiación de proyectos de ayuda, y se quiere financiar la<br />

realización del máximo número de proyectos posible.<br />

Para ello, se dispone de una serie de tipos de proyecto diferentes, con la<br />

siguiente información disponible:<br />

Presupuesto del tipo de proyecto, pi<br />

Número de regiones en las que se puede realizar, ri<br />

Mediante la técnica de programación dinámica, diseña un algoritmo que<br />

permita determinar cuántos proyectos se pueden realizar sin superar el<br />

importe total del fondo, indicando lo siguiente:<br />

1 Estructura de datos intermedia (0,5 puntos)<br />

2 Relación recursiva entre casos y subcasos (1 punto)<br />

3 Código del algoritmo (3,5 puntos)<br />

Yolanda García, Jesús Correas (DSIC - UCM) 30 / 32


Solución ejercicio 8. Programación dinámica<br />

1) La estructura de datos intermedia es una tabla C[1..n, 0..fondo] con<br />

tantas filas como tipos de proyectos, ordenados en orden creciente de<br />

presupuesto, y tantas columnas como el importe del fondo. C[i, j]<br />

representa el número máximo de proyectos (entre los i primeros tipos<br />

de proyecto) que se pueden financiar con un importe j.<br />

2) La relación recursiva entre casos y subcasos es la siguiente:<br />

⎧<br />

−∞ si j < 0<br />

⎪⎨ 0 si j = 0<br />

C(i, j) = ⌊j/pi⌋ si i = 1 y j ≤ piri<br />

ri<br />

si i = 1 y j > piri<br />

⎪⎩ máx {k + C(i − 1, j − k ∗ pi)} en otro caso<br />

0≤k≤ri<br />

donde pi es el presupuesto del proyecto i, p1 ≤ p2 ≤ ... ≤ pn, y ri es<br />

el número de regiones en las que se puede realizar el proyecto.<br />

Yolanda García, Jesús Correas (DSIC - UCM) 31 / 32


Solución ejercicio 8. Programación dinámica (cont.)<br />

fun ong(p[1..n],r[1..n],Fondo)<br />

crear C[1..n,0..Fondo]<br />

desde pr ← 1 hasta n hacer<br />

C[pr,0] ← 0<br />

desde impte ← 1 hasta Fondo hacer<br />

si pr=1 ∧ impte ≤ p[1]*r[1] entonces C[pr,impte] ← ⌊impte/p[1]⌋<br />

si no si pr=1 entonces C[pr,impte] ← r[1]<br />

si no<br />

C[pr,impte] ← 0<br />

desde k ← 0 hasta mín(r[pr],⌊impte/p[pr]⌋) hacer<br />

si C[pr,impte] < k+C[pr-1,impte-k*p[pr]] entonces<br />

C[pr,impte] ← k+C[pr-1,impte-k*p[pr]]<br />

fin si<br />

fin desde<br />

fin si<br />

fin desde<br />

fin desde<br />

devolver C[n,Fondo]<br />

fin fun<br />

Yolanda García, Jesús Correas (DSIC - UCM) 32 / 32

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!