Actividad Práctica#2

Descargar como pdf o txt
Descargar como pdf o txt
Está en la página 1de 18

Universidad Técnica de Manabí

Facultad de Ciencias Informáticas


Carrera de Sistemas de Información

Asignatura:
Análisis y Diseño de Algoritmos

Componente:
Actividades Prácticas y de Experimentación

Integrantes:
➢ Arteaga Arteaga Yanelly Doménica
➢ Fernández Peralta Cesar Augusto
➢ Navarrete Barragán Jhon Eduardo
➢ Molina Guillem Fernando José
➢ Serrano Macias Ernesto David
➢ Zambrano Bravo Merly Paola

Actividad:
Resolver los siguientes problemas:
1. Muestre los valores d y π que resultan de ejecutar la búsqueda de amplitud primero
en el grafo dirigido de la siguiente figura, utilizando el vértice 3 como origen.

2. Muestre los valores d y π que resultan de ejecutar la búsqueda de amplitud primero


en el grafo no dirigido de la siguiente figura, utilizando el vértice u como origen.
Supongamos quelos vecinos de un vértice se visitan en orden alfabético.

1/5
Universidad Técnica de Manabí
Facultad de Ciencias Informáticas
Carrera de Sistemas de Información
3. Muestre que usar un solo bit para almacenar cada color de vértice es suficiente
argumentando que el procedimiento BFS produce el mismo resultado si se elimina
la línea 18. Luego muestre cómo obviar la necesidad de colores de vértice por
completo.

Los colores de los vértices se utilizan principalmente para rastre ar el estado de


cada vértice durante el recorrido. BLANCO indica un vértice no descubierto, GRIS
indica un vértice que ha sido descubierto, pero no explorado completamente y
NEGRO indica un vértice que ha sido explorado completamente. Dado que BFS solo
requiere la información sobre si un vértice ha sido descubierto o no, un solo bit es
suficiente para almacenar esta información. Esto se puede lograr utilizando un
indicador booleano para cada vértice o mediante un enfoque de bits para
representar los colores.

Eliminar la línea 18 del código proporcionado significaría que los vértices nunca se
marcan como explorados completamente. Esto significa que no podrías diferenciar
entre los vértices que están detrás de la frontera y aquellos que están
completamente explorados. Sin embargo, el recorrido central de BFS seguiría
funcionando como se esperaba, ya que se basa en la cola (líneas 10-17) y no
depende explícitamente de los colores de los vértices.

Se puede eliminar la necesidad de los colores de los vértices utilizando un enfoque


diferente para rastrear el estado de los vértices. Un método común es utilizar un
arreglo para rastrear la distancia de cada vértice desde el vértice fuente, donde una
distancia de ∞ indica un vértice no descubierto. También puedes usar un arreglo
de predecesores para rastrear el vértice padre que llevó al descubrimiento del
vértice actual. De esta manera, puedes lograr los mismos resultados de recorrido
BFS sin necesidad de colores de vértices explícitos.

def BFS(G, s):

for u in G.V - {s}:

u.d = float('inf')

u.π = None

s.d = 0

s.π = None

Q = []

Q.append(s)

visitados = set()

visitados.add(s)

while Q:

u = Q.pop(0)

for v in G.Adj[u]:

2/5
Universidad Técnica de Manabí
Facultad de Ciencias Informáticas
Carrera de Sistemas de Información
if v not in visitados:

v.d = u.d + 1

v.π = u

Q.append(v)

visitados.add(v)

4. ¿Cuál es el tiempo de ejecución de un BFS si representamos su grafo de entrada


mediante una matriz de adyacencia y modificamos el algoritmo para manejar esta
forma de entrada?

En una matriz de adyacencia, cada celda (i, j) representa una arista entre el vértice
i y el vértice j. Si el grafo es no dirigido y no ponderado, la matriz de adyacencia es
simétrica, y cada entrada contiene un 0 o un 1 para indicar la ausencia o presencia
de una arista.

from collections import deque

def BFS_matriz_de_adyacencia(matriz, inicio):


n = len(matriz) # Tamaño de la matriz de adyacencia
visitados = [False] * n # Lista de visitados para llevar un seguimiento de los
vértices visitados
distancia = [-1] * n # Lista de distancias desde el vértice de inicio
cola = deque()

visitados[inicio] = True
distancia[inicio] = 0
cola.append(inicio)

while cola:
u = cola.popleft() # Obtenemos el vértice en el frente de la cola
for v in range(n):
# Verificamos si hay una arista entre u y v en la matriz de adyacencia
if matriz[u][v] == 1 and not visitados[v]:
visitados[v] = True
distancia[v] = distancia[u] + 1
cola.append(v)

return distancia

# Ejemplo de uso:
matriz_de_adyacencia = [
[0, 1, 1, 0, 0],
[1, 0, 1, 1, 0],
[1, 1, 0, 0, 1],
[0, 1, 0, 0, 0],
[0, 0, 1, 0, 0]
]

inicio = 0 # Vértice de inicio

3/5
Universidad Técnica de Manabí
Facultad de Ciencias Informáticas
Carrera de Sistemas de Información
distancias = BFS_matriz_de_adyacencia(matriz_de_adyacencia, inicio)
print("Distancias desde el vértice {} a todos los demás vértices:".fo rmat(inicio))
print(distancias)

En general, el tiempo de ejecución de BFS en una matriz de adyacencia se puede


expresar como O(V^2), donde V representa el número de vértices en el grafo. Esto
se debe a que para cada vértice, BFS debe verificar todas las posibles conexiones a
otros vértices, lo que implica una complejidad de tiempo cuadrática en función del
número de vértices.

5. Argumente que, en una búsqueda de primero en amplitud, el valor u.d asignado a un


vértice u es independiente del orden en que aparecen los vértices en cada lista de
adyacencia. Usando el grafo del ejercicio 2, muestre que el primer árbol de anchura
calculado por BFS puede depender del orden dentro de las listas de adyacencia.

Los valores de distancia en BFS son independientes


del orden en las listas de adyacencia, pero el árbol
de búsqueda en anchura en sí puede depender del
orden en el que se seleccionen los vecinos de un
vértice en las listas de adyacencia en caso de empate
en la distancia.

6. Dé un ejemplo de un grafo dirigido G = (V, E), un vértice fuente s ∈ V, y un


conjunto de aristas de árbol Eπ ⊆ E tal que para cada vértice v ∈ V, el único camino
simple en el grafo (V, Eπ) desde 𝑠 hasta 𝑣 es un camino más corto en G, sin embargo,
el conjunto de aristas Eπ no se puede producir ejecutando un BFS en G, No importa
cómo se ordenen los vérticesen cada lista de adyacencia.

Consideremos un grafo dirigido G = (V, E) con los siguientes vértices y aristas:

V = {s, a, b, c, d} E = {(s, a), (s, b), (a, c), (a, d), (b, c), (c, d)}

Ahora, elijamos s como el vértice fuente.

Para crear el conjunto de aristas del árbol Eπ de manera que para cada vértice v ∈ V,
el camino simple único en el grafo (V, Eπ) desde s hasta v sea un camino más corto
en G, podemos elegir las siguientes aristas:

Eπ = {(s, a), (s, b), (a, c), (a, d)}

Ahora, analicemos por qué este conjunto de aristas del árbol Eπ no se puede obtener
ejecutando BFS en G, sin importar cómo ordenemos los vértices en las listas de
adyacencia.

4/5
Universidad Técnica de Manabí
Facultad de Ciencias Informáticas
Carrera de Sistemas de Información
Cuando ejecutamos BFS desde el vértice fuente s en este grafo G, BFS visitará los
vértices en el orden s, a, b, c, d. Sin embargo, BFS producirá el siguiente conjunto de
aristas del árbol:

Aristas del árbol BFS = {(s, a), (s, b), (a, c), (a, d), (b, c)}

Comparando las aristas del árbol BFS con nuestras Eπ elegidas, podemos ver que no
son iguales. Específicamente, el árbol BFS incluye una arista adicional (b, c) que no
está en Eπ. Esto significa que el conjunto de aristas Eπ que elegimos no se puede
obtener ejecutando BFS en este grafo G, independientemente de cómo ordenemos
los vértices en las listas de adyacencia.

7. Hay dos tipos de luchadores profesionales: "caras" (abreviatura de "babyfaces", es


decir, "chicos buenos") y "talones" ("chicos malos"). Entre cualquier par de
luchadores profesionales, puede haber o no una rivalidad. Se le dan los nombres
de n luchadores profesionales y una lista de r pares de luchadores para los que hay
rivalidades. Proporcione un algoritmo de tiempo O(n + r) que determine si es
posible designar a algunos de los luchadores como caras y al resto como talones
de tal manera que cada rivalidad sea entre una cara y un talón. Si es posible realizar
tal designación, su algoritmo debería producirlo.

from collections import defaultdict

def es_posible_designacion(n, rivalidades):

grafo = defaultdict(list)

# Construir el grafo bipartito basado en las rivalidades

for rivalidad in rivalidades:

luchador1, luchador2 = rivalidad

grafo[luchador1].append(luchador2)

grafo[luchador2].append(luchador1)

# Inicializar el diccionario de colores para los luchadores

colores = {}

def colorear_luchador(luchador, color):

colores[luchador] = color

for rival in grafo[luchador]:

if rival in colores:

# Si el rival ya tiene un color asignado, verificamos que no haya conflicto


de colores

if colores[rival] == color:
5/5
Universidad Técnica de Manabí
Facultad de Ciencias Informáticas
Carrera de Sistemas de Información
return False

else:

# Si el rival no tiene un color asignado, lo coloreamos con el color opuesto

if not colorear_luchador(rival, 1 - color):

return False

return True

# Colorear los luchadores

for luchador in range(1, n + 1):

if luchador not in colores:

if not colorear_luchador(luchador, 0):

return "No es posible designar caras y talones."

# Separar a los luchadores en caras y talones

caras = [luchador for luchador, color in colores.items() if color == 0]

talones = [luchador for luchador, color in colores.items() if color == 1]

return "Es posible designar caras y talones.\nCaras: {}\nTalones:


{}".format(caras, talones)

# Ejemplo de uso:

n = 5 # Número de luchadores

rivalidades = [(1, 2), (2, 3), (3, 4), (4, 5), ( 5, 1)] # Pares de luchadores con
rivalidades

resultado = es_posible_designacion(n, rivalidades)

print(resultado)

Este algoritmo realiza BFS para colorear a los luchadores de manera que ningún par
de luchadores adyacentes tenga el mismo color (cara y villano). Si es imposible
hacerlo, devuelve "Imposible". De lo contrario, devuelve las caras y los villanos
designados.

8. El diámetro de un árbol T = (V, E) se define como máximo {δ(u, v) ∶ u, v ∈ V}, es decir,


la mayor de todas las distancias de trayectoria más cortas en el árbol.
Proporcione un algoritmo eficiente para calcular el diámetro de un árbol y analice
el tiempo de ejecución de su algoritmo.

La idea es comenzar desde un vértice arbitrario, realizar dos recorridos DFS y la


6/5
Universidad Técnica de Manabí
Facultad de Ciencias Informáticas
Carrera de Sistemas de Información
profundidad máxima encontrada durante estos recorridos te dará el diámetro del
árbol.

from collections import defaultdict

class Grafo:

def __init__(self):

self.grafo = defaultdict(list)

def agregar_arista(self, u, v):

self.grafo[u].append(v)

self.grafo[v].append(u)

def DFS(self, inicio):

visitados = set()

pila = [(inicio, 0)] # Usamos una pila para DFS y llevamos un seguimiento de la
distancia

maxima_distancia = (inicio, 0)

while pila:

nodo, distancia = pila.pop()

visitados.add(nodo)

if distancia > maxima_distancia[1]:

maxima_distancia = (nodo, distancia)

for vecino in self.grafo[nodo]:

if vecino not in visitados:

pila.append((vecino, distancia + 1))

return maxima_distancia

def diametro_del_arbol(self):

# Encuentra el vértice más alejado desde cualquier punto (paso 2)

vertice_mas_lejano1, _ = self.DFS(list(self.grafo.keys())[0])

# Encuentra el vértice más alejado desde el vértice encontrado anteriormente


(paso 3)

7/5
Universidad Técnica de Manabí
Facultad de Ciencias Informáticas
Carrera de Sistemas de Información
vertice_mas_lejano2, diametro = self.DFS(vertice_mas_lejano1)

return diametro

# Ejemplo de uso:

arbol = Grafo()

arbol.agregar_arista(1, 2)

arbol.agregar_arista(1, 3)

arbol.agregar_arista(2, 4)

arbol.agregar_arista(2, 5)

arbol.agregar_arista(3, 6)

arbol.agregar_arista(5, 7)

arbol.agregar_arista(7, 8)

diametro = arbol.diametro_del_arbol()

print("El diámetro del árbol es:", diametro)

9. Haga un gráfico de 3 por 3 con etiquetas de fila y columna BLANCO, GRIS y NEGRO.
En cada celda (i, j), indique si, en cualquier punto durante una búsqueda en
profundidad de un grafo dirigido, puede haber una arista desde un vértice de color i
hasta un vértice de color j. Para cada arista posible, indique qué tipos de arista
puede ser. Haga un segundo gráfico de este tipo para la búsqueda de profundidad de
un gráfico no dirigido.

Grafo dirigido:

Nodo j
Desde/Hasta Blanco Gris Negro
Árbol
Atrás Atrás
Blanco Cruz
Adelante Cruz
Cruz
Nodo Árbol
i Árbol Árbol
Atrás
Gris Adelante Cruz
Adelante
Cruz Adelante
Cruz
Árbol
Atrás Cruz
Negro Cruz Adelante
Atrás

8/5
Universidad Técnica de Manabí
Facultad de Ciencias Informáticas
Carrera de Sistemas de Información
Grafo no dirigido:

Nodo j
Desde/Hasta Blanco Gris Negro
Árbol Árbol
Nodo Blanco Atrás Atrás
i Árbol Árbol Árbol
Gris Atrás Atrás Atrás
Árbol Árbol
Negro Atrás Atrás

10. Muestre cómo funciona la búsqueda en profundidad en el grafo de la siguiente


figura. Supongamos que el ciclo desde-hasta de las líneas 5–7 del procedimiento DFS
considera los vértices en orden alfabético y suponga que cada lista de adyacencia
está ordenada alfabéticamente. Muestra los tiempos de descubrimiento y
finalización de cada vértice, y muestra la clasificación de cada arista.

Se registraron los tiempos de descubrimiento (d) y finalización (f) de cada vértice, así
como la clasificación de las aristas:
(q): Vértice q se descubre primero (d=1) y se finaliza después (f=14). Las aristas que
salen de q son de tipo árbol.
(s): Vértice s se descubre después de q (d=2) y se finaliza antes (f=5). La arista de q a
s es de tipo árbol.
(v): Vértice v se descubre después de s (d=3) y se finaliza antes (f=4). La arista de s a
v es de tipo árbol.
(w): Vértice w se descubre después de s (d=6) y se finaliza antes (f=9). Las aristas de
q a w y de v a w son de tipo árbol.
(z): Vértice z se descubre después de x (d=11) y se finaliza después (f=12). La arista
de x a z es de tipo árbol.
(x): Vértice x se descubre después de t (d=10) y se finaliza después (f=13). La arista
de t a x es de tipo árbol.
(t): Vértice t se descubre después de q (d=7) y se finaliza antes (f=8). La arista de q a t
es de tipo árbol.
(y): Vértice y se descubre después de t (d=15) y se finaliza después (f=16). Las aristas
de t a y y de r a y son de tipo árbol.
(r): Vértice r se descubre después de t (d=17) y se finaliza antes (f=18). Las aristas de
t a r y de r a u son de tipo árbol.
(u): Vértice u se descubre después de r (d=19) y se finaliza después (f=20). La arista
de r a u es de tipo árbol.
11. Aplique y muestra la estructura de agrupación entre paréntesis de la búsqueda
en profundidad para el siguiente grafo.

9/5
Universidad Técnica de Manabí
Facultad de Ciencias Informáticas
Carrera de Sistemas de Información

(u)
(v)
(y)
(x)
(w)
(z)
(u)
(v)
(y)
(w)
(z)
(x)

Es decir, según las aristas su clasificación sería la siguiente:

(u): Vértice u se descubre por primera vez.


(v): Vértice v se descubre después de u y forma parte del subárbol DFS de u.
(x): Vértice x se descubre después de u y forma parte del subárbol DFS de u.
(y): Vértice y se descubre después de v y forma parte del subárbol DFS de v. La arista
de v a y es de tipo árbol.
(z): Vértice z se descubre después de y y forma parte del subárbol DFS de y. La arista
de y a z es de tipo retroceso.
12. Demuestre que el uso de un solo bit para almacenar cada color de vértice es
suficiente argumentando que el procedimiento DFS produce el mismo resultado si
se elimina la línea 10 de DFS-VISIT.
DFS-VISIT(G, u)
time = time + 1 // El vértice blanco U acaba de ser descubierto
u.d = time
u.color = GRIS
desde cada vértice v en G.Adj[u] // Explora cada arista (U, V)
si v.color == BLANCO
v.π = u
DFS-VISIT(G,v)
time = time + 1
u.f = time
10 u.color = NEGRO // ennegrecer u; Está terminado

DFS-VISIT(G, u)
time = time + 1 // El vértice blanco U acaba de ser descubierto
u.d = time
u.color = GRIS
desde cada vértice v en G.Adj[u] // Explora cada arista (U, V)
si v.color == BLANCO
v.π = u
DFS-VISIT(G,v)
time = time + 1
u.f = time
10
La eliminación de la línea 10, que cambia el color del vértice a "NEGRO" al concluir
su exploración en el algoritmo DFS, tiene principalmente la función de indicar que
el vértice ha finalizado su procesamiento y no tiene más aristas por explorar. Esto
ayuda a mantener un seguimiento del estado de los vértices durante la ejecución

10
/5
Universidad Técnica de Manabí
Facultad de Ciencias Informáticas
Carrera de Sistemas de Información
del algoritmo. No altera el resultado correcto del algoritmo DFS en cuanto a los
tiempos de descubrimiento y finalización de los vértices, ni en lo que respecta a la
clasificación de las aristas. Esto se debe a que los tiempos de descubrimiento y
finalización continúan registrándose de manera adecuada en las líneas 2 y 9,
respectivamente, y la línea 3 todavía marca el vértice como "GRIS" al ser
descubierto, indicando que se encuentra en proceso.

13. Mostrar que, en un grafo dirigido, la arista (u, v) es

a) una arista árbol o una arista de avance si y sólo si u.d < v.d < v.f < u.f,

Si se realiza la búsqueda de profundidad desde el vértice A:


A.d = 1, A.f = 6
B.d = 2, B.f = 3
D.d = 4, D.f = 5
C.d = 7, C.f = 8
La arista A -> B es una arista de árbol porque A.d < B.d < B.f
< A.f (1 < 2 < 3 < 6).
La arista B -> D es una arista de avance porque B.d < D.d <
D.f < B.f (2 < 4 < 5 < 3).
La arista A -> C es una arista de avance porque A.d < C.d <
C.f < A.f (1 < 7 < 8 < 6).
b) una arista de regreso posterior si y sólo si v.d ≤ u.d < u.f ≤ v.f, y

Nos guiamos con el grafo anterior:


La arista D -> B es una arista de regreso posterior porque D.d ≤ B.d < B.f ≤ D.f (4 ≤ 2
< 3 ≤ 5).
c) una arista de cruce si y sólo si v.d < v.f < u.d < u.f.

Nos guiamos con el grafo anterior:


La arista C -> B es una arista de cruce porque C.d < B.d < B.f < C.f (7 < 2 < 3 < 8).

14. Vuelva a escribir el procedimiento DFS, utilizando una pila para eliminar la recursión.

def DFS(G):
for u in G.V:
u.color = BLANCO
u.π = NIL

time = 0
stack = [] # Utilizamos una pila en lugar de la recursión

for u in G.V:
if u.color == BLANCO:
stack.append(u)

while stack:
current_vertex = stack[-1]

if current_vertex.color == BLANCO:
time += 1
current_vertex.d = time
current_vertex.color = GRIS

11
/5
Universidad Técnica de Manabí
Facultad de Ciencias Informáticas
Carrera de Sistemas de Información
found = False
for v in G.Adj[current_vertex]:
if v.color == BLANCO:
v.π = current_vertex
stack.append(v)
found = True
break

if not found:
stack.pop()
time += 1
current_vertex.f = time
current_vertex.color = NEGRO

Este procedimiento DFS utiliza una pila para llevar a cabo la búsqueda en
profundidad de manera no recursiva. Al encontrar un vértice blanco, lo marcamos
como gris, lo exploramos y agregamos sus vecinos blancos a la pila. Cuando no hay
más vecinos blancos, marcamos el vértice como negro y lo eliminamos de la pila.
Esto se repite hasta que se haya explorado todo el grafo.

15. Dé un contraejemplo a la conjetura de que, si un grafo dirigido G contiene un


camino de u a v, y si u.d < v.d en una búsqueda de profundidad de G, entonces v es un
descendiente de u en el bosque producido por la búsqueda primero en
profundidad.

A -> B
A -> C
B -> D
C -> D
Ahora, supongamos que queremos encontrar un camino desde A a D en este grafo
utilizando una búsqueda en profundidad (DFS).
Comenzamos la búsqueda en A y recorremos las aristas en orden alfabético:
Desde A, visitamos B (A -> B) y marcamos B como visitado.
Desde B, visitamos D (B -> D) y marcamos D como visitado.
En este punto, hemos encontrado un camino de A a D: A -> B -> D.
Ahora, veamos los tiempos de descubrimiento (d) de los vértices:
A: d(A) = 1
B: d(B) = 2
D: d(D) = 3
Vemos que u.d (A.d) es menor que v.d (D.d), ya que 1 < 3. Sin embargo, en el
bosque producido por la búsqueda en profundidad, D no es un descendiente de A.
De hecho, D se encuentra en un nivel más bajo que A en el grafo de búsqueda en
profundidad.

16. Dé un contraejemplo a la conjetura de que, si un grafo dirigido G contiene un


camino de u a v, entonces cualquier búsqueda de primero en profundidad debe
resultar en v.d ≤ u.f.

Se muestra el siguiente contraejemplo de un grafo dirigido:

A -> B
A -> C
B -> D
C -> D
12
/5
Universidad Técnica de Manabí
Facultad de Ciencias Informáticas
Carrera de Sistemas de Información
Ahora, supongamos que queremos encontrar un camino desde A a D en este grafo
utilizando una búsqueda en profundidad (DFS).
Comenzamos la búsqueda en A y recorremos las aristas en orden alfabético:
Desde A, visitamos B (A -> B) y marcamos B como visitado.
Desde B, visitamos D (B -> D) y marcamos D como visitado.
En este punto, hemos encontrado un camino de A a D: A -> B -> D.
Ahora, veamos los tiempos de descubrimiento y finalización de los vértices:
A: Descubrimiento (A.d) = 1, Finalización (A.f) = 4
B: Descubrimiento (B.d) = 2, Finalización (B.f) = 3
D: Descubrimiento (D.d) = 3, Finalización (D.f) = 2
Entonces, v.d (D.d) es mayor que u.f (A.f), lo que contradice la afirmación de que
v.d debe ser menor o igual que u.f para cualquier búsqueda en profundidad. En
este caso, tenemos un camino de A a D en el grafo, pero v.d (D.d) es mayor que u.f
(A.f).

17. Modifique el pseudocódigo para la búsqueda primero en profundidad de modo para


que junto a cada asista del grafo dirigido imprima su tipo. Muestre qué
modificaciones, si las hay,que debe realizar si G no es un grafo dirigido.

Algoritmo BusquedaEnProfundidad(G)
visitados = lista vacía

para cada vértice v en G:


si v no está en la lista de visitados:
visitar(v, G, visitados)

FinAlgoritmo

Procedimiento visitar(v, G, visitados)


agregar v a la lista de visitados

para cada vértice adyacente w de v en G:


si w no está en la lista de visitados:
imprimir "Arista (" + v + ", " + w + ") - Tipo: Dirigida"
visitar(w, G, visitados)
sino:
imprimir "Arista (" + v + ", " + w + ") - Tipo: No Dirigida"

En este algoritmo, se utiliza un procedimiento llamado "visitar" que se llama


recursivamente para visitar los vértices adyacentes de un vértice dado. Cuando se
visita una arista dirigida, se imprime la arista junto con su tipo "Dirigida". Si se
visita una arista que ya ha sido visitada, se imprime la arista junto con su tipo "No
Dirigida".

18. Explique cómo un vértice u de un grafo dirigido puede terminar en un árbol de


profundidad que contiene solo u, aunque u tenga aristas entrantes y salientes en G.

Un vértice u en un grafo dirigido puede terminar siendo el inicio de un árbol de


búsqueda en profundidad (DFS, por sus siglas en inglés) que contiene solo a u,
incluso si u tiene aristas de entrada y salida en el grafo G. Esto puede ocurrir en
situaciones en las que las aristas de entrada y salida de u están desconectadas de la
parte principal del grafo o no son alcanzables desde u a través de una búsqueda en
profundidad.

13
/5
Universidad Técnica de Manabí
Facultad de Ciencias Informáticas
Carrera de Sistemas de Información
La clave para entender esto es que un árbol de búsqueda en profundidad se
construye explorando las aristas de salida de un vértice en particular antes de
retroceder y explorar otras ramas. Si las aristas de salida desde u no conducen a
otros vértices que puedan alcanzarse a través de una búsqueda en profundidad, el
árbol de búsqueda resultante solo incluirá al vértice u.

19. Sea G = (V, E) un grafo conexo no dirigido. Proporcione un algoritmo de tiempo O(V
+ E) para calcular un camino en G que atraviese cada arista en E exactamente una
vez en cada dirección. Describa cómo puede encontrar la manera de salir de un
laberinto si se le da un gran suministro de centavos.

Para calcular un camino en un grafo conexo no dirigido que atraviese cada borde en
E exactamente una vez en cada dirección, puedes utilizar el algoritmo del recorrido
en profundidad (DFS, por sus siglas en inglés) modificado. Aquí hay un algorit mo de
tiempo O(V + E) para lograr esto:

1. Crea una lista vacía llamada "camino" para almacenar el camino resultante.

2. Selecciona un vértice inicial arbitrario y marca todos los bordes como no


visitados.

3. Inicia el DFS desde el vértice inicial. Para cada borde no visitado que
encuentres durante el recorrido, añádelo al camino y marca el borde como
visitado.

4. Si llegas a un vértice que no tiene más bordes no visitados, retrocede al


último vértice con bordes no visitados y continúa el recorrido desde allí.

5. Repite los pasos 3 y 4 hasta que hayas visitado todos los bordes del grafo.

6. El resultado será el camino almacenado en la lista "camino".

20. Mostrar cómo usar una búsqueda de profundidad de un grafo G no dirigido para
identificar los componentes conectados de G, de modo que el bosque de
profundidad primero contenga tantos árboles como G tiene componentes
conectados. Más precisamente, muestra cómo modificar la búsqueda de
profundidad primero para que asigne a cada vértice v una etiqueta entera v.cc entre
1 y k, donde k es el número de componentes conectados de G, tal que u.cc = v.cc si y
solo si u y v pertenecen al mismo componente conectado.

Para modificar el algoritmo de búsqueda primero en profundidad y asignar una


etiqueta entera a cada vértice que representa la componente conexa a la que
pertenece, podemos utilizar los siguientes pasos.

1. Inicializar un contador conectadas k a 0.

2. Para cada vértice v en el grafo G, si v no ha sido visitado, llamar a la función


DFS-CC(v, k) para explorar la componente conexa a la que pertenece v.

3. En la función DFS-CC(v, k): Asignar a v una etiqueta entera v.cc igual al valor
actual de k. Marcar v como visitado. Para cada vecino u de v, si u no ha sid o
visitado, llamar recursivamente a DFS-CC(u, k).

4. Incrementar el valor de k en 1 después de cada llamada a DFS -CC.


14
/5
Universidad Técnica de Manabí
Facultad de Ciencias Informáticas
Carrera de Sistemas de Información
Al finalizar este proceso, cada vértice v del grafo G tendrá asignada una etiqueta v.cc
que representa la componente conexa a la que pertenece. Dos vértices u y v tendrán
la misma etiqueta v.cc si y solo si pertenecen a la misma componente conexa.

EJEMPLO EN PSEUDOCODIGO

DFS-CC(v, k):
v.cc = k
marcar v como visitado
para cada vecino u de v:
si u no ha sido visitado:
DFS-CC(u, k)

Búsqueda-CC(G):
k=0
para cada vértice v en G:
si v no ha sido visitado:
k=k+1
DFS-CC(v, k)

21. Muestra el orden de los vértices producidos por TOPOLOGICAL-SORT cuando se


ejecuta en el dag de la siguiente figura. Supongamos que el bucle for de las líneas 5–
7 del procedimiento DFS considera los vértices en orden alfabético y suponga que
cada lista de adyacencia está ordenada alfabéticamente.

Orden de selección de vértices y las listas de adyacencia ordenadas


alfabéticamente, el orden en el que se seleccionarán los vértices durante el
ordenamiento topológico:

Selecciona "m" porque no tiene padres.


Selecciona "n" después de "m".
Selecciona "o" después de "n".
Selecciona "p" después de "o".
Selecciona "q" después de "p".
Selecciona "r" después de "q".
Selecciona "s" después de "q" y "p".
Selecciona "t" después de "u".
Selecciona "u" después de "n".
Selecciona "v" después de "y".
Selecciona "w" después de "v".
Selecciona "x" después de "m".
Selecciona "y" después de "u".
Selecciona "z" después de "w".

Por lo tanto, el orden resultante del ordenamiento topológico, siguiendo las reglas
que mencionaste y las listas de adyacencia ordenadas alfabéticam ente, sería: m, n,
o, p, q, r, s, t, u, v, w, x, y, z.

22. Dé un algoritmo de tiempo lineal que, dado un grafo acíclico dirigido G = (V, E) y
dos vértices a, b ∈ V, devuelve el número de caminos simples de a hacia b en G. Por
ejemplo, el grafo acíclico dirigido de la figura en el ejercicio anterior contiene
exactamente cuatro caminos simples desde el vértice p hasta el vértice v: 〈p, o, v〉,

15
/5
Universidad Técnica de Manabí
Facultad de Ciencias Informáticas
Carrera de Sistemas de Información
〈p, o, r, y, v〉, 〈p, o, s, r, y, v〉 y 〈p, s, r, y, v〉. Su algoritmo solo necesita contar las rutas
simples, no enumerarlas.

Este algoritmo utiliza una lista llamada "visitados" para almacenar los vértices
visitados durante el recorrido y una cola para realizar la búsqueda en amplitud.
Comienza eligiendo un vértice inicial arbitrario y lo agrega tanto a la lista de
"visitados" como a la cola. Luego, repite el proceso de sacar un vértice de la cola y
agregar sus vértices adyacentes no visitados a la lista de "visitados" y a la cola. Al
final, verifica si todos los vértices del grafo están en la lista de "visitados" para
determinar si el grafo es conexo individualmente.

Algoritmo EsConexoIndividualmente(G)
visitados = lista vacía
cola = cola vacía

elegir un vértice inicial arbitrario u


agregar u a la lista de visitados
agregar u a la cola

mientras la cola no esté vacía:


v = sacar un vértice de la cola

para cada vértice adyacente w de v:


si w no está en la lista de visitados:
agregar w a la lista de visitados
agregar w a la cola
si todos los vértices del grafo están en la lista de visitados:
retornar verdadero
sino:
retornar falso
FinAlgoritmo

23. Proporcione un algoritmo que determine si un grafo no dirigido G = (V, E) contiene


un ciclo simple. El algoritmo debe ejecutarse en tiempo O(V), independientemente
de |E|.

#include <stdbool.h>

bool tieneCicloSimpleDFS(int G[][V], int actual, bool visitados[], int padre[]) {


visitados[actual] = true;

for (int i = 0; i < V; i++) {


if (G[actual][i]) { // Si hay una arista entre actual y i
if (!visitados[i]) {
padre[i] = actual;
if (tieneCicloSimpleDFS(G, i, visitados, padre)) {
return true;
}
} else if (i != padre[actual]) {
return true;
}
}
}

16
/5
Universidad Técnica de Manabí
Facultad de Ciencias Informáticas
Carrera de Sistemas de Información
return false;
}

bool tieneCicloSimple(int G[][V]) {


bool visitados[V];
int padre[V];
for (int i = 0; i < V; i++) {
visitados[i] = false;
padre[i] = -1;
}

for (int v = 0; v < V; v++) {


if (!visitados[v]) {
if (tieneCicloSimpleDFS(G, v, visitados, padre)) {
return true;
}
}
}

return false;
}

int main() {
// Definir tu grafo como matriz de adyacencia G
// int G[V][V] = ...

if (tieneCicloSimple(G)) {
printf("El grafo contiene un ciclo simple.\n");
} else {
printf("El grafo no contiene un ciclo simple.\n");
}
return 0;
}

24. Probar o refutar: Si un grafo dirigido G contiene ciclos, entonces el orden de vértices
producido por TOPOLOGICAL-SORT(G) minimiza el número de aristas "malas" que
son inconsistentes con el orden producido.

Se considera verdadero, si un grafo dirigido G contiene ciclos, entonces el


ordenamiento de vértices producido por TOPOLOGICAL-SORT(G) minimiza la
cantidad de "aristas malas" que son inconsistentes con el ordenamiento producido.

25. Otra forma de ordenar topológicamente un grafo acíclico dirigido G = (V, E) es


encontrar repetidamente un vértice de grado 0, emitirlo y eliminarlo y todas sus
aristas salientes del grafo. Explicar cómo implementar esta idea para que se ejecute
en el tiempo O(V + E). ¿Qué sucede con este algoritmo si G tiene ciclos?

a b
\ /
\/
c
/\
d e

17
/5
Universidad Técnica de Manabí
Facultad de Ciencias Informáticas
Carrera de Sistemas de Información
Algoritmo OrdenTopologicoKahn(G):
Inicializar cola
Inicializar OrdenTopologico

Calcular grados de entrada para todos los vértices

Para cada vértice v en V:


Si grado de entrada de v es 0:
Agregar v a la cola

Mientras la cola no esté vacía:


Sacar un vértice v de la cola
Agregar v a OrdenTopologico
Para cada vértice u adyacente a v:
Decrementar el grado de entrada de u
Si grado de entrada de u es 0:
Agregar u a la cola

Si todos los vértices se han agregado a OrdenTopologico:


Devolver OrdenTopologico
Sino:
El grafo contiene ciclos

Grafo ejemplo:
V = {a, b, c, d, e}
E = {(a, c), (b, c), (c, d), (c, e)}

OrdenTopologicoKahn(Grafo)

18
/5

También podría gustarte

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy