Solución parcial 2 Dante 9 de junio de 2009 Ejercicio 1 El profesor Germán volcó cerveza en el teclado de su laptop, y se arruinaron todas las teclas que denotan signos de puntuación, además de la barra espaciadora. Ayer me escribió un mail cuyo cuerpo era: AdjuntoejerciciosparaelparcialSaludos Dado un Germán-mensaje como una cadena s[1..n], se le pide determinar si el mensaje puede reconstruirse como una secuencia válida de palabras de nuestro idioma. Por ejemplo, el mensaje anterior es una secuencia válida, pues las palabras “Adjunto”, “ejercicios”, “para”, “el”, “parcial”, “Saludos” son todas válidas. Un ejemplo de secuencia no válida es holatychau. Para ayudarse, usted contará con un diccionario, que será representado mediante una función booleana dic : String → Bool , que determina si una secuencia de caracteres está en el diccionario. Es decir, true si s[p..q] es válida dic(s[p..q]) = false si no Solución (a) La idea es ir leyendo la cadena de derecha a izquierda, y ver cuándo la subcadena s[j..n] se puede rearmar como un mensaje. Cuando j = 1, obtenemos la respuesta. Supongamos que tenemos la cadena s[j..n] ¿Cuándo es válida? El caso más fácil de responder es si dic(s[j..n]) = true, pues la secuencia completa es una palabra y no hace falta intercalar espacios. Si s[j..n] no está en el diccionario, entonces necesariamente existe una posición donde hay que poner un espacio. Tenemos que verificar Que se puede armar una palabra s[j..k] que está en el diccionario Que la secuencia que queda, s[k + 1..n] es una Germán-mensaje válido Todo esto para algún j ≤ k < n. La formulación recursiva es entonces: true G(s[j..n]) = ∃k ∈ {j, . . . , n − 1} dic(s[j..k]) ∧ G(s[k + 1..n]) 1 si dic(s[j..n]) = true en caso contrario (b) El algoritmo de programación dinámica serı́a más o menos ası́: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 procedure Ger ( s ) n ← length ( s ) tabla [ 1 . . n ] ← { false , . . . , false } f o r j ← n downto 1 do i f d i c ( s [ j . . n ] ) then t a b l a [ j ] ← true else f o r k ← j to n − 1 do i f d i c ( s [ j . . k ] ) ∧ t a b l a [ k+1] then t a b l a [ j ] ← true i f tabla [ 1 ] r e t u r n true else return false (c) Es fácil ver que la condición en la lı́nea 9 se evalúa 1+2+. . . n−1 = n×(n−1) 2 veces, y asumiendo que la complejidad de dic es Θ(1), la complejidad de Ger resulta Θ(n2 ). Se deja un análisis más detallado como ejercicio. Ejercicio 2 Sea X un conjunto finito de intervalos cerrados de la recta real. Diremos que un conjunto de puntos P atraviesa a X si cada intervalo de X contiene al menos un punto en P . Por ejemplo, en la figura vemos un conjunto de intervalos X, y un conjunto P de cuatro puntos (vistos como barras verticales) que atraviesa a X. Dadas dos secuencias L[1..n] y R[1..n], que representan los extremos izquierdos y derechos de n intervalos, estamos interesados en encontrar un conjunto de mı́nimo cardinal que lo atraviese. Por ejemplo, dadas las secuencias L = h4, 5, 0, 11, 1, 10, 5i R = h7, 10, 3, 14, 5, 13, 9i Los intervalos serán X = {[4, 7], [5, 10], [0, 3], [11, 14], [1, 5], [10, 13], [5, 9]}, y un conjunto que atraviesa X es P = {2, 6, 11}. (a) Describa una estrategia greedy que encuentre una solución al problema (b) Demuestre que su estrategia es óptima 2 Solución (a) Tomemos todos los intervalos. Imaginemos un conjunto P que cumple con las condiciones, y pensemos en su punto más a la izquierda (un análisis similar vale para el punto más a la derecha). Para intentar cubrir lo más posible, podemos pensar que nos conviene que este punto esté lo más a la derecha posible, porque ası́ tiene más posibilidades de cubrir más intervalos. Por supuesto, no puede ser tan a la derecha como uno quiera, porque va a llegar un punto en que va a dejar de cubrir algún intervalo Pregunta: ¿Cuándo sucede esto? Respuesta: Cuando el punto llega al final del primer intervalo, donde llamamos primer intervalo al que termina primero, leyendo de izquieda a derecha (es decir, aquel de menor valor para R). A ver si se entiende con un ejemplo: Para la figura del enunciado, podemos correr a la derecha la primer barra vertical, siempre y cuando no pase el final de ningún intervalo. Nuestra estrategia greedy será entonces: (1) Ordenar los intervalos de forma creciente de acuerdo a sus extremos derechos (2) Mientras queden intervalos sin cubrir, elegir como punto para agregar a P el extremo derecho del primer intervalo “tachar” todos los intervalos que son cubiertos por este punto ¡Es la misma solución que para el problema de las tareas! Para el ejemplo dado en el problema, luego de ordenar queda: X = {[0, 3], [1, 5], [4, 7], [5, 9], [5, 10], [10, 13], [11, 14]} En el primer paso, elegimos el punto 3, y tachamos dos intervalos: [0, 3] y [1, 5]. Ahora los intervalos que quedan son: X = {[4, 7], [5, 9], [5, 10], [10, 13], [11, 14]} Elegimos ahora el punto 7, y tachamos [4, 7], [5, 9] y [5, 10]. Los intervalos que quedan son: X = {[10, 13], [11, 14]} elegimos el punto 13 y tachando ya no quedan intervalos. Juntando todo, tenemos que el conjunto P que atraviesa X es P = {3, 7, 11} (b) Demostremos que es óptima. Daremos las ideas principales, dejando los detalles para el lector. 3 Seguridad de la estrategia greedy Debemos ver que el punto que elegimos en cada paso forma parte de una solución óptima. Supongamos que tenemos los intervalos X = {xj , . . . , xn }, donde inicialmente j = 1. Debemos ver que existe una solución optima que tiene al punto R[j] (extremo derecho del primer intervalo). Sea P = {p1 , . . . , pk } una solución óptima al problema, y pi el mı́nimo de P. Observemos que pi ≤ R[j], pues xj debe ser atravesado, y por lo tanto debe existir un punto en P que esté a la izquierda de R[j]. Como pi es el más a la izquierda de P , concluimos que pi está a la izquierda de R[j]. Tomemos ahora P 0 = P \ {pi } ∪ {R[j]} ¿Es P 0 solución óptima para X? P 0 tiene la misma cantidad de elementos que P , por lo que si es solución, es óptima R[j] tiene que atravesar al menos tantos intervalos como pi : Si no fuese ası́, es porque hay algún intervalo que pi atravesaba y R[j] no; pero esto significa que el final de este intervalo está a la izquierda de R[j], lo cual es absurdo, pues R[j] era el final “más a la izquierda” que habı́a. Por lo tanto P 0 es solución para X. Concluimos entonces que R[j] pertenece a un óptimo para el problema. Subestructura óptima Sea X = {xj , . . . , xn } un conjunto de intervalos ordenados, R[j] el extremo derecho de xj , y X 0 = {xk ∈ X | L[k] > R[j]} Es decir, X 0 es el subproblema que queda luego de elegir R[j]. Tenemos que ver que, si P 0 = {p1 , . . . , pm } es solución óptima de X 0 , entonces P = P 0 ∪ {Rj } es solución óptima de X. Supongamos que no, entonces existe Q solución de X tal que |Q| < |P |. Sea qi el mı́nimo de Q. Como Q es solución de X, necesariamente tiene que ser qi < R[j], pues Q tiene que atravesar xj . De esto se deduce que qi no atraviesa ningún intervalo en X 0 (ya que en X 0 todos empiezan a la derecha de R[j]). Por lo tanto, Q0 = Q \ {qi } tiene que atravesar X 0 . Pero si esto es cierto, encontramos un conjunto Q0 que es solución de X 0 y además |Q0 | = |Q| − 1 < |P | − 1 = |P 0 |. Absurdo, pues P 0 era óptimo de X 0. 4