# Final 2021.09.16 ###### tags: `Finales` * Resolución de Lebron: https://t.me/c/1461330951/3408 ## Resumen ### Glosario/definiciones ### Trucos * En dinámica muy probablemente necesites 2 índices (y te queda una matriz) ## Resolución ### 1) ![ejercicio_1](https://i.imgur.com/XjGZZPo.png) ![función matemática](https://i.imgur.com/xGWpW2d.jpg) ```python S = [0, 15, 40, 50, 60] # S = [0, 25, 30, 55, 70] M = [["UNDEFINED" for _ in range(len(S))] for i in range(len(S))] def f(pos_actual, pos_anterior): print(f"anterior: {S[pos_anterior]}, actual: {S[pos_actual]},") if M[pos_actual][pos_anterior] != "UNDEFINED": return M[pos_actual][pos_anterior] if pos_actual == len(S) - 1: M[pos_actual][pos_anterior] = cumplen_la_condicion(pos_actual, pos_anterior) if pos_actual >= 1 and cumplen_la_condicion(pos_actual, pos_anterior): M[pos_actual][pos_anterior] = f(pos_actual + 1, pos_actual) or f(pos_actual + 1, pos_anterior) if pos_actual >= 1 and not cumplen_la_condicion(pos_actual, pos_anterior): M[pos_actual][pos_anterior] = f(pos_actual + 1, pos_anterior) return M[pos_actual][pos_anterior] def cumplen_la_condicion(pos_actual, pos_anterior): return 15 <= S[pos_actual] - S[pos_anterior] <= 25 print(f(1, 0)) # imprimo la matriz for i in M: for j in i: print(j, end=' ') print() ``` El algoritmo evalua todos los subconjuntos posibles y devuelve verdadero si y solo sí hay uno que cumple la condición. La implementación usa 2 índices para recorrer el conjunto de números. En uno tenemos la posición actual y en otro la anterior. La misma empieza en las posiciones 0 y 1. Se fija si los elementos en esas ubicaciones cumplen con la condición. * Si no la cumplen, sabe que el elemento actual debe ser removido para llegar a una solución válida. Se continúa la recursión quitando al actual. * Si cumplen la condición, se debe chequear los 2 casos posibles: * Continuar manteniendo al elemento actual * Continuar quitando al elemento actual Cuando se llega a las últimas 2 posiciones, se chequea si estos elementos cumplen la condición. El resultado da True si estos también la cumplen. De lo contrario, el resultado será False. Complejidad: $O(|S|²)$ Tenemos |S|² estados posibles, y cada estado se calcula 1 vez en $O(1)$. Ya que la siguiente vez que se necesite, se utilizará el resultado memoizado. ### 2) ```python INFINITO = float('inf') V = {'A', 'B', 'C', 'D', 'E', 'F'} E = {(('A', 'D'), 2), (('A', 'B'), 3), (('A', 'C'), 1), (('D', 'E'), 1), (('C', 'E'), 2), (('C', 'F'), 2)} def dame_w_distancia_minima(S, π): π_ordenado = dict(sorted(π.items(), key=lambda item: item[1])) for w in π_ordenado: if w not in S: return w def vecinos_sin_camino_minimo_calculado(w, S): result = set() for e in E: if e[0][0] == w and e[0][1] not in S: result.add(e[0][1]) if e[0][1] == w and e[0][0] not in S: result.add(e[0][0]) return result def distancia(v, w): for e in E: if e[0] == (v, w) or e[0] == (w, v): return e[1] # no hace falta pero por las dudas print('warning: devolviendo distancia de no adyacentes') return INFINITO def hay_eje_entre(v, w): for e in E: if e[0] == (v, w) or e[0] == (w, v): return True return False print(dijkstra('A')) ``` #### (a) Explique cómo modificar el algoritmo de Dijkstra para contar el numero de diferentes caminos mínimos de $v$ a $w$ ```python def dijkstra(origen): S = set() π = {} pred = {} cant = {} for v in V: π[v] = INFINITO pred[v] = INFINITO cant[v] = 1 π[origen] = 0 pred[origen] = 0 while S != V: w = dame_w_distancia_minima(S, π) S.add(w) for v in vecinos_sin_camino_minimo_calculado(w, S): distancia_candidata = π[w] + distancia(v, w) if distancia_candidata == π[v]: cant[v] += 1 if distancia_candidata < π[v]: π[v] = distancia_candidata pred[v] = w return (π, pred, cant) ``` #### (b) Explique como modificar el algoritmo de Dijkstra de modo tal que si hay mas de un camino mínimo de $v$ a $w$, se elija el camino con el menor numero de aristas ```python def dijkstra(origen): S = set() π = {} pred = {} cant_caminos = {} cant_aristas = {} for v in V: π[v] = INFINITO pred[v] = INFINITO cant_caminos[v] = 1 cant_aristas[v] = 0 π[origen] = 0 pred[origen] = 0 while S != V: w = dame_w_distancia_minima(S, π) S.add(w) for v in vecinos_sin_camino_minimo_calculado(w, S): distancia_candidata = π[w] + distancia(v, w) if distancia_candidata == π[v]: cant_caminos[v] += 1 if cant_aristas[w] + 1 < cant_aristas[v]: pred[v] = w cant_aristas[v] += cant_aristas[w] + 1 if distancia_candidata < π[v]: π[v] = distancia_candidata pred[v] = w cant_aristas[v] += cant_aristas[w] + 1 return (π, pred, cant_caminos, cant_aristas) ``` ### 3) Un grafo $G$ es coloreable en forma única si todo coloreo con $χ(G)$ colores induce la misma particion de los vertices. Mostrar que si $G$ es coloreable en forma ́unica el subgrafo inducido por dos conjuntos cualesquiera de la particion inducida por los $χ(G)$-coloreos es un subgrafo conexo ### 4) Justificar todas las respuestas. #### a) Dado un problema de decisión $Π$, si mostramos una reduccion polinomial de $Π$ a $TSP$ y otra de $TSP$ a $Π$, decidir si cada una de las siguientes sentencias se desprenden de estos hechos: > Voy a usar X en esto para referirme a \Pi. ##### i. $Π$ es $NP-hard$ pero no $NPC$. > i) Falso. X es NP-completo. Si hay una reducción polinomial de TSP a X, entonces X es al menos tán difícil como TSP, luego X está en NP-Hard. Si hay una reducción polinomial de X a TSP, entonces como se puede resolver TSP en una máquina de Turing no-determinista en tiempo polinomial, se puede resolver X en una máquina de Turing no-determinista en tiempo polinomial, y luego X está en NP. Como está en NP, y está en NP-Hard, está en NP-complete. ##### ii. $Π$ es $NP$ pero no $NPC$. > ii) Falso, por el mismo motivo. ##### iii. $Π$ es $NPC$. > iii) Cierto, por el mismo motivo. ##### iv. $Π$ no es $NP-hard$ ni $NPC$. > iv) Falso, por el mismo motivo. ##### v. $Π$ esta en $P$. > v) No se sabe. Si P = NP, entonces al X estar en NP, X estaría en P. Si P != NP, entonces hay problemas en NP que son más difíciles que cualquier cosa en P, y como X está en NP-hard, es igual o más dificil que ellos, y luego X no está en P. > [name=Lebron] [time=Sun, Dec 22, 2021 5:25 AM] #### b) Decidir si cada una de las siguientes sentencias es verdadera o falsa: ##### i. Un problema $Π ∈ NP$ es NP-completo si puede ser reducido a $SAT$ en tiempo polinomial. Falso. Para ser NPC, tiene que ser NP y NP-hard. Que $Π$ pueda ser reducido a SAT no implica que $Π$ pertenezca a NP-hard. Si la reducción fuera al revés, de SAT a $Π$, podríamos afirmar que pertenece a NPC por el teorema de Cook-Levin. ##### ii. Un problema $Π ∈ NP$ es NP-completo si $SAT$ puede ser reducido a ́el en tiempo polinomial. Verdadero: Ver inciso anterior. ##### iii. Si $NP = coNP$ entonces $P = NP$. Falso: que todo problema cuyas instancias positivas puedan ser certificadas polinomialmente, cumpla que sus instancias negativas también puedan serlo y viceversa, no implica que existan algoritmos polinomiales para resolverlos. ##### iv. Si $NP ≠ coNP$ entonces $P ≠ NP$. Afirmación equivalente a su contrarrecíproca: $P = NP => NP = coNP$ Verdadero: Si $P = NP$, significa que para todo problema $NP$ existe un algoritmo polinomial que lo resuelve. De esta forma, podemos certificar instancias (tanto negativas como positivas) en tiempo polinomial usando el certificado trivial (la instancia) y el algoritmo polinomial que lo resuelve como verificador.