Ejercicios resueltos
Ejercicios resueltos
Ejercicios resueltos
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