Ejercicios_Resueltos_Grafos

28
Grafos Sea A un conjunto de k enteros aleatorios elegidos dentro de un rango de 1 a 100. Se define una Relación entre los elementos de A tal que R= {xRy, si x ^ y son múltiplos de tres; donde xєA, yєA } Es decir si 3 está en A y 9 está en A entonces 3R9. Pero si 9 está en A y 8 está en A estos elementos no están en la relación ya que 8 no es múltiplo de tres. El problema anterior se puede representar con un grafo en donde cada vértice representa un numero aleatorio y cada par (x,y) es un arco siempre que x-y sea múltiplo de 3, a usted se le solicita: a) Escriba una función que genere un grafo con las condiciones descritas anteriormente. b) Calcule el grafo de entrada y el grado de salida de cada uno de los vértices El texto nos pide resolver varias cosas así que vamos a descomponer el problema de tal forma que se nos haga más fácil resolverlo. Primero vamos a definir un TDA DataVertex que será el que almacene la información de los grados de un vértice. Además tendrá un campo de tipo int que será donde se guarde el valor aleatorio que debemos generar. Y por último también declararemos una variable entera id para identificar a cada vértice, este campo podría ser innecesario si siempre tenemos en cuenta que cada vértice del grafo es único. Primero vamos a implementar una función la cual llamaremos: Graph*crearGrafo() que crea un vértice, genera un numero aleatorio entre uno y cien, lo almacena en dicho vértice y luego lo agrega al grafo. No sabemos cuantos elementos hay en A es decir no se nos detalla cuantos vértices tiene el grafo así que asumimos un valor de N que podría ser igual a 10, 20 o lo que sea. El prototipo de la función solicitada será void unirVertices(Graph*grafo); que tendrá como objetivo unir dos vértices siempre y cuando los valores que almacenen ambos sean múltiplos de tres. Por ejemplo un vértice que almacena el numero 3 estará conectado con otro vértice que almacene el número 9, ya que 3 y 9 son múltiplos de tres. Lo primero que hace unirVertices es llamar a la función crearGrafo que hemos descrito anteriormente. Luego vamos buscando los vértices del grafo en donde el valor almacenado sea múltiplo de tres. Cuando encontramos uno, utilizamos la variable GVertex*inicio, para guardar la referencia a ese vértice. Ahora hace falta buscar el vértice destino. Volvemos a recorrer el grafo y cuando encontremos un vértice que cumpla la condición ya descrita, lo referenciamos con la variable Gvertex*destino. La linea: graphLinkVertices(grafo, inicio, destino); crea un arco en el grafo desde el vértice inicio hasta el vértice destino. Se hace este procedimiento para todos los vértices del grafo. Observe que se corre con el riesgo de que un vértice se conecte con sí mismo, para resolver esto usamos la variable id, de esta forma cuando encontremos un vértice destino para conectar nos aseguraremos que no sea el mismo vértice inicio, también pudimos haber resuelto este problema validando directamente que las dos referencias inicio, destino no son iguales. Ahora hace falta calcular el grado de entrada y salida de un vértice. El grado de entrada de un vértice es el número de arcos que entran a él. El grado de salida de un vértice es el número de arcos que salen de él. Por ejemplo considere en siguiente grafo G: El vértice V2 tiene grado de salida igual a cero ya que no tiene vértices adyacentes pero su grado de entrada es igual a tres ya que V2 es adyacente a otros tres vértices. El vértice V1 tiene grado de salida igual a uno y grado de entrada igual a uno. Así mismo el vértice V3 tiene un grado de salida igual a dos y grado de entrada igual a cero. El vértice V4 tiene grado de entrada igual a dos y grado de salida igual a uno. El vértice V5 tiene grado de salida igual a uno y grado de entrada igual a cero. Y finalmente el vértice V6 que tiene grado de salida igual a dos y grado de entrada igual a uno. Rodrigo Castro Reyes Facultad de Ingeniería Eléctrica y Computación

description

ejercicios resuelto sobre grafos usando librerias y estructuras de datos en c

Transcript of Ejercicios_Resueltos_Grafos

Page 1: Ejercicios_Resueltos_Grafos

Grafos

Sea A un conjunto de k enteros aleatorios elegidos dentro de un rango de 1 a 100. Se define una Relación entre loselementos de A tal que R= {xRy, si x ^ y son múltiplos de tres; donde xєA, yєA } Es decir si 3 está en A y 9 está en Aentonces 3R9. Pero si 9 está en A y 8 está en A estos elementos no están en la relación ya que 8 no es múltiplo detres. El problema anterior se puede representar con un grafo en donde cada vértice representa un numero aleatorioy cada par (x,y) es un arco siempre que x-y sea múltiplo de 3, a usted se le solicita:

a) Escriba una función que genere un grafo con las condiciones descritas anteriormente.

b) Calcule el grafo de entrada y el grado de salida de cada uno de los vértices

El texto nos pide resolver varias cosas así que vamos a descomponer el problema de tal forma que se nos haga másfácil resolverlo. Primero vamos a definir un TDA DataVertex que será el que almacene la información de los grados deun vértice. Además tendrá un campo de tipo int que será donde se guarde el valor aleatorio que debemos generar. Ypor último también declararemos una variable entera id para identificar a cada vértice, este campo podría serinnecesario si siempre tenemos en cuenta que cada vértice del grafo es único.

Primero vamos a implementar una función la cual llamaremos: Graph*crearGrafo() que crea un vértice, genera unnumero aleatorio entre uno y cien, lo almacena en dicho vértice y luego lo agrega al grafo. No sabemos cuantoselementos hay en A es decir no se nos detalla cuantos vértices tiene el grafo así que asumimos un valor de N quepodría ser igual a 10, 20 o lo que sea.

El prototipo de la función solicitada será void unirVertices(Graph*grafo); que tendrá como objetivo unir dos vérticessiempre y cuando los valores que almacenen ambos sean múltiplos de tres. Por ejemplo un vértice que almacena elnumero 3 estará conectado con otro vértice que almacene el número 9, ya que 3 y 9 son múltiplos de tres.

Lo primero que hace unirVertices es llamar a la función crearGrafo que hemos descrito anteriormente. Luego vamosbuscando los vértices del grafo en donde el valor almacenado sea múltiplo de tres. Cuando encontramos uno,utilizamos la variable GVertex*inicio, para guardar la referencia a ese vértice. Ahora hace falta buscar el vérticedestino. Volvemos a recorrer el grafo y cuando encontremos un vértice que cumpla la condición ya descrita, loreferenciamos con la variable Gvertex*destino. La linea: graphLinkVertices(grafo, inicio, destino); crea un arco en elgrafo desde el vértice inicio hasta el vértice destino. Se hace este procedimiento para todos los vértices del grafo.Observe que se corre con el riesgo de que un vértice se conecte con sí mismo, para resolver esto usamos la variable id,de esta forma cuando encontremos un vértice destino para conectar nos aseguraremos que no sea el mismo vérticeinicio, también pudimos haber resuelto este problema validando directamente que las dos referencias inicio, destinono son iguales.

Ahora hace falta calcular el grado de entrada y salida de un vértice. El grado de entrada de un vértice es el número dearcos que entran a él. El grado de salida de un vértice es el número de arcos que salen de él. Por ejemplo considere ensiguiente grafo G:

El vértice V2 tiene grado de salida igual a cero ya que no tiene vértices adyacentes pero su grado de entrada es igual atres ya que V2 es adyacente a otros tres vértices. El vértice V1 tiene grado de salida igual a uno y grado de entradaigual a uno. Así mismo el vértice V3 tiene un grado de salida igual a dos y grado de entrada igual a cero. El vértice V4tiene grado de entrada igual a dos y grado de salida igual a uno. El vértice V5 tiene grado de salida igual a uno y gradode entrada igual a cero. Y finalmente el vértice V6 que tiene grado de salida igual a dos y grado de entrada igual a uno.

Rodrigo Castro ReyesFacultad de Ingeniería Eléctrica y Computación

Page 2: Ejercicios_Resueltos_Grafos

Teniendo claro lo anterior ya podemos pensar en como diseñar un algoritmo que calcule los grados de entrada y salidade todos los vértices del grafo. Para hacerlo mas sencillo lo vamos a hacer en dos procedimientos: voidcalcularGradoDeEntrada(Graph*grafo) y void calcularGradoDeSalida(Graph*grafo).

Para el calculo del grado de salida de un vértice v basta con conocer el tamaño de la lista de adyacencia de v.Recordemos que en la lista de adyacencia están todos los arcos que van del vértice v a los vertices vk,vk+1,etc.

Para el calculo del grado de entrada de un vértice v, tenemos que buscar a cuantos vértices es adyacente v. Es decircon la referencia a v vamos ir buscando en las listas de adyacencia del resto de vértices si existe un arco que conecte aesos vértices con v. El código a continuación es una posible solución del problema descrito.

#define MAX 100#define N 10

typedef struct DataVertex { int value; int id; int grado_entrada; int grado_salida;} DataVertex;

DataVertex*dataVertexNew(int i) { DataVertex*data = malloc(sizeof (DataVertex)); data->value = i; data->grado_salida = 0; data->grado_entrada = 0; data->id = 0; return data;}int dataComparar(Generic a,Generic b){ DataVertex*x=(DataVertex*)a; DataVertex*y=(DataVertex*)b; if(x->value==y->value)return 0; return 1;}Graph*crearGrafo() { Graph*grafo = graphNew(); int i = 0; srand(time(NULL)); while (i < N) { int x = rand() %MAX+1; DataVertex *data = dataVertexNew(x); data->id = i; GVertex *vertice = gVertexNew(data); GVertex*buscado=graphSearchVertex(grafo,vertice->content,dataComparar);//antes de añadir se busca si ya existe if(buscado==NULL){ graphAddVertex(grafo, vertice); i++; }//si el vertice ya existe no se añade al grafo } return grafo;}

void calcularGradoDeSalida(Graph*grafo) { NodeList*it = NULL; GVertex*vertice_actual = NULL; DataVertex*data = NULL; for (it = listGetHeader(grafo); it != NULL; it = nodeListGetNext(it)) {

Rodrigo Castro ReyesFacultad de Ingeniería Eléctrica y Computación

Page 3: Ejercicios_Resueltos_Grafos

vertice_actual = nodeListGetCont(it); data = (DataVertex*) gVertexGetContent(vertice_actual);//cast data->grado_salida = listGetSize(gVertexGetAdjacents(vertice_actual)); //obtiene el tamaño de la lista de adyacencia de vertice_actual }}

void calcularGradoDeEntrada(Graph*grafo) { NodeList*it = NULL,*p=NULL; GVertex*vertice_actual = NULL,*vertice_origen=NULL; DataVertex*data = NULL; for (it = listGetHeader(grafo); it != NULL; it = nodeListGetNext(it)) { vertice_actual = nodeListGetCont(it); data = (DataVertex*) gVertexGetContent(vertice_actual); for(p= listGetHeader(grafo); p != NULL; p = nodeListGetNext(p)){ vertice_origen=nodeListGetCont(p); GEdge*e=graphGetLink(vertice_origen,vertice_actual); if(e!=NULL){//existe un arco desde vertice_origen hasta vertice_actual data->grado_entrada++; //aumenta el grado de entrada de vertice_actual }//si no existe este arco se seguira buscando con el resto de vertices } }}

void unirVertices(Graph*grafo) { NodeList*it = NULL, *p; GVertex*vertice_actual = NULL, *inicio = NULL, *destino = NULL; DataVertex*data = NULL; for (it = listGetHeader(grafo); it != NULL; it = nodeListGetNext(it)) { vertice_actual = nodeListGetCont(it); data = (DataVertex*) gVertexGetContent(vertice_actual);//cast if (data->value % 3 == 0) { inicio = vertice_actual; p=listGetHeader(grafo); while (p != NULL) { GVertex*v = nodeListGetCont(p); DataVertex*d = (DataVertex*) gVertexGetContent(v);//cast if (d->value % 3==0 && d->id != data->id) {

//si el contenido de v es multiplo de tres y no se trata del mismo vertice inicio destino = v; graphLinkVertices(grafo, inicio, destino,0,NULL);//une inicio con destino } p = nodeListGetNext(p); } } } calcularGradoDeSalida(grafo); calcularGradoDeEntrada(grafo);}

Rodrigo Castro ReyesFacultad de Ingeniería Eléctrica y Computación

Page 4: Ejercicios_Resueltos_Grafos

La municipalidad de Guayaquil ha decidido crear un nuevo sistema de transporte, que se basa en estaciones debuses en la ciudad, conectadas entre ellas, a través de recorridos específicos. Desde cada estación se puede llegar aotras en forma directa(sin pasar por otras estaciones). La idea de la municipalidad es ofrecer un servicio adicional alos usuarios, instalando en cada estación un sistema que permita al usuario conocer si existe o no forma de llegar deuna estación A a otra B, y en caso de que sea factible; porque estaciones intermedias pasara.Una red de transporte de n estaciones puede estar representada por una matriz de nxn. Cada estación estaríaidentificada por el indice de las filas de la matriz. Una celda indicaría si hay o no camino directo entre dos estacionesa través de un 1 o un 0.a) Defina de forma correcta los TDAs Red y Estación para este caso.b) Escriba una función que dada una red de transporte y dos estaciones, retorne si existe o no camino entre lasmismas y en caso de que exista, porque estaciones esta compuesto.Solución:a) Nuestra Red de Transporte sera un grafo implementado con una matriz de ceros y unos. En donde los subindices delas filas o columnas representaran las estaciones de la red. Dado que no sabemos el numero de vértices, nuestramatriz sera dinámica.b) En la matriz de adyacencia se encuentra la información sobre los pares de vértices que tienen conexión directa. Porejemplo para el siguiente grafo:

La matriz de adyacencia sera la siguiente:

El concepto de camino se puede resumir en una secuencia de vértices que inicia desde un vértice Vi y termina en unvértice Vj.

Rodrigo Castro ReyesFacultad de Ingeniería Eléctrica y Computación

Page 5: Ejercicios_Resueltos_Grafos

La función que debemos implementar nos tiene que retornar un TRUE o FALSE dependiendo de si existe o no uncamino entre cualquier par de vértices del grafo y ademas debemos almacenar dicho camino.

Por el ejemplo si el vértice inicio fuera el vértice uno y el vértice destino el vértice dos, entonces nuestra función deberetornar TRUE. Ademas el camino seria la secuencia {V1,V2} Esto lo podemos deducir fácilmente si observamos lamatriz de adyacencia. Ahora suponga el caso de que el vértice inicio es el vértice dos, y el destino es el vértice cinco, basados en lainformación proporcionada por la matriz de adyacencia vemos que no existe una conexión directa entre estos dosvértices, pero eso no significa que no exista un camino entre estos dos vértices. Observe la figura que representa algrafo, note que desde el vértice dos, si podemos llegar al vértice seis, pasando previamente por el vértice cinco. Esdecir si puedo llegar desde el vértice dos hasta el vértice cinco, y ademas puedo llegar desde el vértice cinco hasta alvértice seis, es razonable decir que podemos ir desde el vértice dos hasta el vértice seis y de hecho el camino seria lasecuencia {V2,V5,V6} Generalizando: Si existe un camino entre Vi y Vk, y existe un camino entre Vk y Vj entonces existeun camino entre Vi y Vj.Como conclusión del análisis previo, debemos partir de la matriz de adyacencia para ir encontrando las posiblesadyacencias, ademas debemos almacenar de alguna forma el camino que se va generando. Lo vamos a hacer de lasiguiente forma: Primero vamos a definir un arreglo de vértices previos:

Este arreglo es muy similar a la fila del vértice inicio en la matriz adyacente, y nos indica los vértices previos para llegara algún otro vértice en particular. Por ejemplo el elemento dos del arreglo (V1) nos indica que para llegar a V2 primerotenemos que pasar por V1. Así mismo el elemento tres del arreglo(V1) nos indica que para llegar a V2 primerotenemos que pasar por V1. El cero en el elemento cuatro nos indica que no existe ningún vértice previo para llegar aV4. Este arreglo ira cambiando su estado a medida que vayamos encontrando nuevos vértices a donde podemos llegar.La fila del vértice inicio en la matriz de adyacencia y el arreglo de vértices previos se irán mejorando a medida que seejecute nuestro algoritmo. A continuación vamos a describir el funcionamiento del mismo con un ejemplo enparticular. Supongamos que necesitamos saber si existe camino entre V1 y V6. Nuestro objetivo sera ir mejorando lafila de V1 y que al final esta información nos diga si existe o no camino. Para esto lo que haremos es analizar si existealguna forma de llegar desde el vértice inicio a todos los vértices del grafo, definidos en el conjunto V={V1,V2,...Vn}Empezamos con el vértice V1. Debido a que toda la columna de V1 es cero, no hay forma que desde otros vérticeslleguemos a V1, esto lo podemos intuir de la figura que presentamos anteriormente.

Rodrigo Castro ReyesFacultad de Ingeniería Eléctrica y Computación

Page 6: Ejercicios_Resueltos_Grafos

Ahora nuestro análisis se enfocara en V2. La columna de V2 nos dice que podemos llegar a este vértice solamentedesde V1, así que no necesitamos cambiar nada. El mismo análisis ocurre para el vértice V3.El siguiente vértice de análisis es V4. La columna de V4 nos dice que desde V3 podemos llegar a este vértice, ahora loque debemos verificar es si existe forma de llegar de V1 a V3, la información en la misma matriz nos dice que sipodemos hacerlo. En conclusión dado que podemos llegar de V1 a V3 y de V3 a V4, entonces la posición de la matrizque indica la adyacencia entre V1 y V4 debe ser cambiada por un uno.

También debemos actualizar el estado del arreglo de vértices previos:

El próximo vértice de análisis es V5. La columna de V5 nos indica que podemos llegar a este vértice desde V2. Dadoque también podemos llegar desde V1 a V2, entonces existe un camino de V1 y V5.

Rodrigo Castro ReyesFacultad de Ingeniería Eléctrica y Computación

Page 7: Ejercicios_Resueltos_Grafos

El análisis en V6 y V7 es exactamente el mismo, al final tendremos lo siguiente:

Ahora ya estamos listo para responder a lo solicitado por el problema, para saber si existe o no un camino desde inicioa algún destino, simplemente verificamos el valor correspondiente en la matriz. Por conclusión vemos que si existe uncamino desde V1 a V6. Pero ¿Cual es ese camino? Para esto nos sirve el arreglo de vértices previos. El arreglo nos diceque para llegar al destino V6, primero debemos pasar por V5, para llegar a V5 primero pasamos por V2, para llegar aV2 primero pasamos por V1 y como V1 es el vértice inicio entonces ya hemos encontrado el camino solicitado.

A continuación se mostrara la implementación de todas estas ideas que se han planteado.Código:typedef struct RedTransporte { int **MatrizAdyacencia;} RedTransporte;

typedef int Estacion;

RedTransporte*crear(int n){ int i; RedTransporte*red=malloc(sizeof(RedTransporte)); red->MatrizAdyacencia=malloc(sizeof(int*)*n);//matriz dinámica de n por n for(i=0;i<n;i++){ red->MatrizAdyacencia[i]=malloc(sizeof(int)); } return red;}

Rodrigo Castro ReyesFacultad de Ingeniería Eléctrica y Computación

Page 8: Ejercicios_Resueltos_Grafos

Hemos asumido que tanto la matriz como el arreglo de vértices anteriores que llegan a esta función, ya han sidoinicializados previamente. La matriz que se recibe por parámetro debe ser una copia de la matriz de adyacencia querepresenta al grafo.

int caminoVertices(int matriz[][N], int verticesAnteriores[], Estacion inicio, Estacion destino,List*camino) { int j, k, source, destination,flag; source = inicio - 1;//indices empiezan desde cero destination = destino - 1; for (j = 0; j < N; j++) { for (k = 0; k < N; k++) { if (matriz[k][j]&&matriz[source][k]) { matriz[source][j] = 1; verticesAnteriores[j] = k + 1; } } } if(matriz[source][destination]){ flag=1; j = 0; k = destination; while (k != source) { j = verticesAnteriores[k] - 1; listAddFirst(camino, nodeListNew(integerNew(k + 1))); k = j; } listAddFirst(camino, nodeListNew(integerNew(source + 1))); }else{ flag=0; printf("No existe camino entre estos pares de vertices") } return flag;}

El grafo que representa la Red de Transporte puede ser de dos tipos: Dirigido: pueden existir solamente arcos en una sola dirección, es decir podemos ir desde un estación A hasta otra Bpero no podemos retornar desde B hasta A, tiene sentido pensar en esto dado que el problema no especifica masdetalles al respecto.No dirigido: podemos asumir que para cara par de estaciones hay camino de ida y vuelta, también tiene sentido estaidea.El algoritmo presentado anteriormente, que encuentra el posible camino entre dos vértices puede fallar dependiendode la naturaleza del grafo. Para ciertos casos es necesario analizar el resto de filas para mejorar otra fila en particular.Dada esta limitación vamos a extender la idea de la implementación del algoritmo anterior la cual se basaba en irmejorando la fila del vértice inicio verificando si podemos llegar a otros vértices pasando por un vértice V k. Ahoraharemos el mismo análisis pero se irán mejorando TODAS las filas de la matriz, de esta forma obtendremos una matrizcon la información sobre la existencia de todos los caminos posibles dentro del grafo (ya no solo desde un inicio), estamatriz es conocida como matriz de caminos.

Rodrigo Castro ReyesFacultad de Ingeniería Eléctrica y Computación

Page 9: Ejercicios_Resueltos_Grafos

Para recuperar el camino se usara la misma idea planteada anteriormente. Piense en que tendría que agregar a lafunción para recuperar todos los caminos desde cualquier par de vértices, observe que si se trata de un grafo en dondeexisten ciclos, existirán varios caminos, dado que el problema es determinar el camino entre Vi y Vj, basta conocer unode todos esos caminos posibles para poder resolver el problema.

int caminoVertices(int matriz[][N], int verticesAnteriores[], GVertex*inicio, GVertex*destino, List*camino) { int i, j, k, source, destination, flag; Estacion*A = (Estacion*) gVertexGetContent(inicio); Estacion*B = (Estacion*) gVertexGetContent(destino); source = *A - 1; destination = *B - 1; //algoritmo de warshall for (k = 0; k < N; k++) { for (i = 0; i < N; i++) { for (j = 0; j < N; j++) { if (matriz[i][k] && matriz[k][j]) { matriz[i][j] = 1; if(verticesAnteriores[j]==0){//solo si es cero se mejora el arreglo verticesAnteriores verticesAnteriores[j] = k + 1; } } } } } //recupero el camino if (matriz[source][destination]) { flag = 1; j = 0; k = destination; while (k != source) { j = verticesAnteriores[k] - 1; listAddFirst(camino, nodeListNew(integerNew(k + 1))); k = j; } listAddFirst(camino, nodeListNew(integerNew(source + 1))); } else { flag = 0; } return flag;}

Rodrigo Castro ReyesFacultad de Ingeniería Eléctrica y Computación

Page 10: Ejercicios_Resueltos_Grafos

Antes de leer el siguiente problema considere lo siguiente:Circuitos Eulerianos: Un Grafo G, no dirigido y sin vértices aislados, tiene un circuito euleriano si existe un circuito enG que recorre cada arista del grafo exactamente una vez.Teorema: Un grafo no dirigido, sin vértices aislados, tiene un circuito euleriano si y solo si G es conexo y todo vérticede G tiene grado par.

Durante el siglo XVII, la ciudad de Konigsberg estaba dividida en cuatro zonas unidas por siete puentes, loshabitantes de esa ciudad se hacían la siguiente pregunta: ¿Existe un camino en que se cruce cada puenteexactamente una vez para regresar al punto de partida? El matemático Euler represento esta situación mediante ungrafo, que se muestra a continuación, en donde pudo deducir que para resolver el problema de los puentes deKonigsberg debíamos encontrar un circuito de Euler.a) Defina los TDA’s necesarios para resolver el problema anteriorb) Implemente una función que retorne True si en un grafo G existe un circuito de Euler y False sino.c) Para el problema de los puentes de Konigsberg ¿existirá dicho circuito? Explique su respuesta.

Para resolver este problema debemos tener bien en cuenta las condiciones que deben cumplirse para que existe uncircuito de Euler en un grafo G. El texto al principio nos detalla un Teorema que en resumen nos dice que si un grafo Ges conexo y todos sus vértices tienen grado par entonces podemos afirmar que G tiene un circuito de Euler ¿Cual es?No lo sabemos depende del grafo en cuestión ¿Podemos encontrarlo? Seguro que si pero eso no nos pidenimplementar, solo debemos retornar verdadero o falso según el caso. Note que para que todo tenga sentido G sera ungrafo no dirigido, no debemos validar esto porque el problema ya nos afirma este hecho. En cuanto al grado delgrafo,en un grafo no dirigido, este valor indica el numero de arcos que salen o entran de un vértice por ejemplo en elGrafo que Euler represento para resolver el problema de los puentes de Konigsberg podemos afirmar que el grado deV1 es igual a 3, o que el grado de V4 es igual a 3. Este hecho ya nos adelanta la respuesta del literal c). Observe que elgrado de entrada y de salida de un vértice en un grafo no dirigido son iguales, entonces para conocer este valordeberíamos simplemente chequear el tamaño la lista de adyacencia del vértice en cuestión.a) Cada vértice representa una de las cuatro zonas en las que esta divida la ciudad, los arcos serán los puentes que lasconectan. No es necesario definir en lenguaje C estos TDAs puesto que no vamos a operar sobre ellos. Lo que si vamosa declarar es un TDA DataVertex que tendrá dos campos uno para almacenar el grado del vértice y el otro sera uncampo genérico contenido.b) El prototipo de la función sera int existeCicloEuler(Graph*grafo) que devuelve 1 o 0 dependiendo del grafo aprocesar. Como ya hemos dicho vamos a verificar las dos condiciones propuestas en el Teorema, para estoimplementaremos tres funciones: una para saber si un grafo es Conexo, otra para calcular el grado de cada vértice yotra para revisar esos valores y retornar verdadero si los grados de todos los vértices del grafo son pares o falso si estono es cierto.Para determinar si un grafo es Conexo básicamente aplicaremos una versión resumida del mismo algoritmo que se usapara saber el numero de componentes conexas de un grafo. Es decir escogemos un vértice Vo cualquiera del grafo,aplicamos un recorrido (Anchura o Profundidad) y lo almacenamos en un conjunto W, verificamos si W=V, V conjuntode vértices, en otras palabras si los vértices del recorrido son los mismos del grafo, si esto es verdad entonces el grafoes conexo, el mismo resultado se obtiene si verificamos que todos los vértices han sido visitados luego del recorrido.No debería ser difícil calcular el grado de cada vértice ya lo describimos al principio. Ahora para verificar la paridad delgrado de cada vértice simplemente iteramos la lista de vértices, y vamos verificando si el grado de cada vértice es par,si esto se cumple para todos retornamos true pero si en algún momento el grado de algún vértice Vk es impardebemos inmediatamente retornar false.

Rodrigo Castro ReyesFacultad de Ingeniería Eléctrica y Computación

Page 11: Ejercicios_Resueltos_Grafos

c) Como vimos anteriormente el grado de V1 es igual a tres y con esto es suficiente para afirmar que en ese grafonunca vamos a encontrar un circuito de Euler.

A continuación se muestra el código implementando las funcionalidades requeridas:

typedef struct DataVertex { Generic contenido; int grado;} DataVertex;

List*graphRecorridoAnchura(Graph*grafo,GVertex* source); int verticesIguales(Graph*G,List*path);int compararVertices(GVertex*v,GVertex*u);

DataVertex*dataVertex(Generic contenido){ DataVertex*data=malloc(sizeof(DataVertex)); data->grado=0; data->contenido=contenido; return data;}

int verticesIguales(Graph*grafo,List*recorrido){//retorna true si todos los vertices del grafo estan en la lista recorrido yfalse si no NodeList*it,*buscado; for(it=listGetHeader(grafo);it!=NULL;it=it->next){ GVertex*vertice=(GVertex*)nodeListGetCont(it); buscado=listSearch(recorrido,vertice,compararVertices);//busca un vertice del grafo en el recorrido if(buscado==NULL){//si en alguna iteracion no encuentra un vertice entonces la lista recorrido no almacena todoslos vertices del grafo return 0; } } return 1;}

int esConexto(Graph*grafo){ List*recorrido=graphRecorridoAnchura(grafo,nodeListGetCont(listGetHeader(grafo))); if(verticesIguales(grafo,recorrido)){ return 1; }else{ return 0; }}int compararVertices(GVertex*v,GVertex*u){ if(v==u)return 0; return 1 ;}

/*otra formaint esConexoDos(Graph*grafo){ List*recorrido=graphRecorridoAnchura(grafo,nodeListGetCont(listGetHeader(grafo))); //Si el grafo fuera conexo se supone que luego de haberse efectuado el recorrido todos los vertices deberian estarvisidados //vamos a comprobar esa condiccion recuerde que estamos tratando con grafos no dirigidos NodeList*it; for(it=listGetHeader(grafo);it!=NULL;it=it->next){

Rodrigo Castro ReyesFacultad de Ingeniería Eléctrica y Computación

Page 12: Ejercicios_Resueltos_Grafos

GVertex*vertice=nodeListGetCont(it); if(vertice->visited==0){ return 0; } } return 1; }*/

int calcularGrado(Graph*grafo){ NodeList*it; for(it=listGetHeader(grafo);it!=NULL;it=it->next){ GVertex*vertice=nodeListGetCont(it); DataVertex*data=(DataVertex*)vertice->content; data->grado=listGetSize(vertice->LAdjacents); if(data->grado%2!=0){ return 0; } } return 1;}

int tieneCicloEuler(Graph*grafo){if(esConexo(grafo)&&calcularGrado(grafo)){

return 1;}else{

return 0;}

}Implementar la función esCompleto, la cual recibe un grafo y retorna true si el grafo es completo sino retorna false.Un grafo es completo si cada uno de los nodos es adyacente a los demás nodos del grafo.Suponga que se tiene a G y G1 dos grafos simples, conexos y no dirigidos, tal y como se muestran mas abajo en lafigura. Recuerde que un grafo es completo si cada uno de los nodos es adyacente a los demás nodos del grafo, estehecho se cumple para el grafo G. En donde los vértices 2,3, y 4 son adyacentes al vértice 1. Así mismo los vértices 1,3,4son adyacentes al vértice 2. Caso similar sucede para el resto de vértices.

Ahora observe el grafo G1, el vértice 1 no es adyacente al vértice 2, esta característica supone que G1 no sera un grafoCompleto. Este ejemplo muy sencillo nos da ciertas pautas muy importantes para resolver el problema. Observe que el grafo Gtiene cuatro vértices, y que cada vértice de G posee exactamente 3 vértices adyacentes. En G1 es claro notar que estehecho no se cumple. Ahora supongamos que tenemos un grafo Gr=(V,E) tal que V={v1,v2,v3,....vn} en donde n es un

Rodrigo Castro ReyesFacultad de Ingeniería Eléctrica y Computación

Page 13: Ejercicios_Resueltos_Grafos

entero positivo mayor que 0 y que puede ser tan grande como se necesitara. Supongamos que no existen lazos en Gr,es decir que un vértice no esta conectado con si mismo. Para que Gr sea un grafo completo cada uno de sus vérticesdebe estar conectado exactamente con n-1 vértices.Esta sera una condición que debemos tener en cuenta para la implementación de la función. Recuerde que es muyimportante tratar de asumir ciertas cosas, descartar detalles irrelevantes y aprovechar los datos que se nosproporciona de manera implícita. Observe que hemos asumido que los grafos analizados serán no dirigidos y conexos,y esto es correcto puesto que un grafo dirigido no puede ser completo. Así mismo un grafo disconexo no puede sercompleto. Otro detalle importante es saber que el grafo es simple ya que con esto nos aseguramos que no existenarcos paralelos dentro del grafo.Una posible solución podría ser la que detallaremos a continuación. Luego de verificar que la condición que sedescribió al principio se cumple, partiremos desde el primer vértice del grafo, al cual lo vamos a identificar con elpuntero vertex. Ahora vamos a volver a iterar el grafo y verificamos con la función : GEdge*graphGetLink(GVertex*source,GVertex*destination) si existen los arcos entre el vértice vertex y el resto de vértices quedefinen el grafo. En cada iteración, referenciamos los vértices con la variable v_adj y verificamos si existe un arco entrevertex y v_adj. Recuerde que debemos asegurarnos que vertex y v_adj no son iguales. Si es que no existe un arco entrevertex y v_adj entonces quiere decir que no existe conexión entre estos vértices por lo cual deberíamos retornar falso.Si graphGetLink nunca nos retorno NULL entonces al final de todas las iteraciones hemos verificado que vertex estaconectado con los n-1 vertices restantes del grafo, debemos verificar que esto es cierto para todos los vértices delgrafo para al final retornar True.Código:int esCompleto(Graph*grafo){ NodeList*it,*p; GVertex*vertex,*v_adj; int tam_grafo,tam_list_vert; tam_grafo=listGetSize(grafo); for(it=listGetHeader(grafo);it!=NULL;it=nodeListGetNext(it)){ vertex=nodeListGetCont(it); tam_list_vert=listGetSize(vertex->LAdjacents); if(tam_list_vert!=tam_grafo-1){ return 0; }else{ p=listGetHeader(grafo);//volveremos a iterar el grafo while(p!=NULL){ v_adj=nodeListGetCont(p); if(vertex!=v_adj){//si los vertices origen y destino son diferentes GEdge*tmp=graphGetLink(vertex,v_adj); if(tmp==NULL){return 0;} } p=nodeListGetNext(p); } } } return 1;}

Rodrigo Castro ReyesFacultad de Ingeniería Eléctrica y Computación

Page 14: Ejercicios_Resueltos_Grafos

Implemente una función esNoDirigido, que reciba un grafo y retorne True si el grafo es no dirigido y False si no loes.Solución:El prototipo de la funcion es: int esNoDirigido(Graph*G); Para implementar la función solicitada haremos lo siguiente: primero referenciamos el primer vértice del grafo con lavariable vertex. Ahora debemos iterar la lista de adyacencia de vertex. Referenciamos con la variable v_adj como elprimer vértice adyacente de vertex. La función definida en la librería: GEdge*graphGetLink(GVertex*source,GVertex*destination) nos dirá si existe un arco incidente entre un vértice origen y undestino. En este caso el vértice origen es v_adj y el destino es vertex. En caso de que no exista este arco, quiere decirque no existe conexión de v_adj a vertex. Esto supone que la función deberá retornar falso. Si es que si existe secontinua analizando con el siguiente vértice adyacente.Se hace esto para todos los vértices del grafo, al final la función retornara uno si es verdadero y cero si es que es falso.

int esNoDirigido(Graph*grafo) { NodeList*it, *p; GVertex*vertex; for (it = listGetHeader(grafo); it != NULL; it = nodeListGetNext(it)) { vertex = nodeListGetCont(it); p = listGetHeader(vertex->LAdjacents); while (p != NULL) { GEdge*e = nodeListGetCont(p); GVertex *v_adj = gEdgeGetDestination(e); GEdge*tmp = graphGetLink(v_adj, vertex); if (tmp == NULL) { return 0; } p = nodeListGetNext(p); } } return 1;}

Rodrigo Castro ReyesFacultad de Ingeniería Eléctrica y Computación

Page 15: Ejercicios_Resueltos_Grafos

Un árbol T es un árbol de expansión de un Grafo G si T es un subgrafo de G que contiene todos los vértices de G.

T1 y T2 son arboles de expansión del grafo G.T3 no es un árbol de expansión del grafo G.Realizar una función cuyo prototipo es int EsArbolDeExpansion(Grafo T,Grafo G) que devuelve 1 si un grafo Tpertenece al conjunto de árbol de expansión o abarcador de un grafo G, devuelve 0 en caso contrario. Considereque solo puede ser generador arboles de expansión de tipo binario a partir del grafo G.Solución:Antes de pensar en la solución del problema, es necesario reforzar algunos conocimientos teóricos. Considere losiguiente: Sea G=(V,E) , es decir un grafo G que esta definido por un conjunto de vértices V y un conjunto de arcos E. SeaG1=(V1,E1). G1 es un subgrafo de G si: V1≠Ø E1⊆EEn otras palabras, G1 sera un subgrafo de G si existe al menos un par de vértices en V 1 y el conjunto de arcos E1 es unsubconjunto del conjunto de arcos que definen a G. Note que esta implícito el hecho de que V 1 también debe ser unsubconjunto de V. Adicionalmente, si V1=V entonces podemos afirmar que G1 es un árbol expansión de G.Como conclusión del análisis previo, para que Gk sea un árbol de expansión de G deben cumplirse dos condiciones: Gk

debe ser un subgrafo de G y todos los vértices de G deben estar en Gk.Analizando los ejemplos que nos plantea el problema podemos ver que el grafo T es un subgrafo de G, y todos losvértices definidos en G, también forman parte de T. En conclusión T es un árbol de expansión del grafo G.Ahora al analizar el grafo T3, vemos que también es un subgrafo de G pero no todos los vértices de G forman parte de

Rodrigo Castro ReyesFacultad de Ingeniería Eléctrica y Computación

Page 16: Ejercicios_Resueltos_Grafos

T3, falta el vértice V4. Entonces T3 no es un árbol expansión de G.Nuevamente para implementar la función que se nos solicita, debemos asegurarnos de dos cosas: Primero que todoslos vértices del grafo G también están en el grafo T. Y también que los arcos que conforman al grafo T están contenidosen G.Presentaremos dos soluciones al problema anterior, en la primera se realiza un análisis vértice a vértice de lascondiciones previamente planteadas, en la segunda se resuelve el problema de forma mas general.Primera Solución:Vamos a iterar el conjunto de vértices de G. Referenciamos al primer vértice, y buscamos si existe en T, si no existe, notiene sentido seguir comprobando según lo que ya hemos expuesto. Si es que si existe, ahora debemos determinar sitodos los elementos del conjunto de arcos del primer vértice de T están en el conjunto de arcos de G. Si es que esto escierto, procedemos a realizar el mismo análisis al siguiente vértice de G. Al final de todo este proceso se comprobara siT es un árbol de expansión de G.

Código:int EsArbolDeExpansion(Graph *arbol, Graph *grafo, cmpfn compararVertices) { int flag ; NodeList*p_grafo, *p_tree,*p; GVertex*v_graph,*v_tree; GEdge*arco_tree,*arco_graph; p_grafo = listGetHeader(grafo); while (p_grafo!=NULL) { v_graph = nodeListGetCont(p_grafo); v_tree = graphSearchVertex(arbol,gVertexGetContent(v_graph), compararVertices); if (v_tree != NULL) { p_tree=listGetHeader(gVertexGetAdjacents(v_tree)); while (p_tree!=NULL) { arco_tree= nodeListGetCont(p_tree); //busqueda de arco_tree en la lista de adyacencia de v_graph flag=0; for(p=listGetHeader(v_graph->LAdjacents);p!=NULL;p=nodeListGetNext(p)){ arco_graph=nodeListGetCont(p); if(compararVertices(gEdgeGetDestination(arco_tree)->content,

gEdgeGetDestination(arco_graph)->content)==0){ flag=1; } } if(flag==0){//nunca encontró arco_tree return flag; } p_tree=nodeListGetNext(p_tree); } } else {// si v_tree es NULL quiere decir que el vertice v_graph no esta en el grafo árbol así que debemos retornar false flag = 0; return flag; } p_grafo=nodeListGetNext(p_grafo); } return flag;}Hay que tener mucho cuidado al operar en los vértices del grafo, evite usar funciones para remover nodos y tengamucho cuidado con el manejo de punteros, ademas el uso inadecuado de las funciones definidas en la librería puedencambiar el estado del grafo. Aunque en este problema no hay peligro de cambiar la naturaleza del grafo, vamos apresentar una solución alternativa en donde usamos colas para almacenar copias de los vértices o arcos, la funciónencolar que recibe una cola y un grafo (que a la final es una lista) hará este trabajo. Como vera no hay muchadiferencia en la solución presentada anteriormente.

Rodrigo Castro ReyesFacultad de Ingeniería Eléctrica y Computación

Page 17: Ejercicios_Resueltos_Grafos

Código:

int EsArbolDeExpansion(Graph *arbol, Graph *grafo, cmpfn compararVertices) { int flag = 0; Queue*queue_grafo = queueNew(); Queue*queue_tree = queueNew(); encolar(queue_grafo, grafo); NodeList*q; GVertex*v_graph,*v_tree,*destino_arbol,*destino_grafo; GEdge*arco; while (!queueIsEmpty(queue_grafo)) { v_graph = nodeListGetCont(queueDequeue(queue_grafo)); v_tree = graphSearchVertex(arbol, v_graph->content, compararVertices); //busca el contenido del vertice v_graphen el grafo arbol if (v_tree != NULL) { encolar(queue_tree, v_tree->LAdjacents); while (!queueIsEmpty(queue_tree)) { arco = nodeListGetCont(queueDequeue(queue_tree)); destino_arbol = gEdgeGetDestination(arco); flag = 0; //busqueda del destino arbol en el grafo g for (q = listGetHeader(v_graph->LAdjacents); q != NULL; q = nodeListGetNext(q)) { destino_grafo = gEdgeGetDestination(nodeListGetCont(q)); if (compararVertices(destino_grafo->content, destino_arbol->content) == 0) { flag = 1; } } if (flag == 0) {//si nunca lo encuentra no es necesario seguir iterando return flag; } } } else {//si no lo encuentra se cambia el valor de la bandera y se rompe el lazo ya que no es necesario seguir iterando flag = 0; break; } } return flag;}

Rodrigo Castro ReyesFacultad de Ingeniería Eléctrica y Computación

Page 18: Ejercicios_Resueltos_Grafos

Segunda Solución:En esta solución, implementaremos dos funciones adicionales: la función: int verticesIguales(Graph*arbol, Graph*grafo, cmpfn compararVertices)que comprobara si los vértices de T son los mismos de Gla función: int esSubGrafo(Graph*arbol, Graph*graph, cmpfn compararVertices)que comprobara si T es un subgrafo de G, para esto vamos a referenciar todos los arcos de T en una estructuratemporal, luego comprobaremos si todos estos arcos pertenecen al conjunto de arcos de G.Si los resultados de estas dos funciones es verdadero, entonces habremos solucionado el problema.Observe que se utiliza un callback en ambas funciones, esto es debido a que no sabemos que tipo de dato almacenacada vértice.

Código:

int verticesIguales(Graph*arbol, Graph*grafo, cmpfn compararVertices) { NodeList*it; GVertex*v_arbol, *v_grafo; int flag ; for (it = listGetHeader(arbol); it != NULL; it = nodeListGetNext(it)) { v_arbol = nodeListGetCont(it); v_grafo = graphSearchVertex(grafo, v_arbol->content,charCmp); //busca v_arbol dentro del otro grafo if (v_grafo == NULL) {//nunca lo encontro, se debe retornar false flag = 0; return flag; } } flag=1; return flag;}

int esSubGrafo(Graph*arbol, Graph*graph, cmpfn compararVertices) { NodeList*p, *q; int flag ; Queue*queue_tree=queueNew(); GVertex*vertice; GEdge*arco_tree; //Utilizaremos una cola como estructura de almacenamiento para hacer mas facil el analisis for (p = listGetHeader(arbol); p != NULL; p = nodeListGetNext(p)) { vertice = nodeListGetCont(p); for(q=listGetHeader(gVertexGetAdjacents(vertice));q!=NULL;q=nodeListGetNext(q)){ arco_tree=nodeListGetCont(q); queueEnqueue(queue_tree, nodeListNew(arco_tree));//ingreso todos los arcos del grafo arbol } } arco_tree=NULL; //vamos a verificar si el conjunto de arcos del grafo arbol es un subconjunto del conjunto de arcos de graph while (!queueIsEmpty(queue_tree)) { arco_tree = nodeListGetCont(queueDequeue(queue_tree)); if(!existeArco(graph,arco_tree,compararVertices)){//esta funcion verificara si arco_tree pertenece al conjunto dearcos de graph flag=0;//si el arco de arbol no existe en graph entonces no tiene sentido seguir iterando return flag; } } flag=1; return flag;}

Rodrigo Castro ReyesFacultad de Ingeniería Eléctrica y Computación

Page 19: Ejercicios_Resueltos_Grafos

int existeArco(Graph*graph,GEdge*arco,cmpfn compararVertices){ int flag=0; GVertex*inicio_grafo; NodeList*it; GEdge*arco_grafo; GVertex*inicio=gEdgeGetSource(arco); GVertex*destino=gEdgeGetDestination(arco); //vamos a verificar si inicio existe en graph inicio_grafo=graphSearchVertex(graph,inicio->content,compararVertices); if(inicio_grafo!=NULL){ //vamos a verificar si destino existe en la lista de adyacencia de inicio_grafo for(it=listGetHeader(gVertexGetAdjacents(inicio_grafo));it!=NULL;it=nodeListGetNext(it)){ arco_grafo=nodeListGetCont(it); if(compararVertices(destino->content,gEdgeGetDestination(arco_grafo)->content)==0){

//si el arco (inicio,destino) existe en el grafo graph debemos retornar true flag=1; return flag; } } }else{ flag=0; return flag; } return flag;}

int esArbolExpansion(Graph*arbol,Graph*graph,cmpfn compararVertices){ int flag; if(verticesIguales(arbol,graph,compararVertices)&&esSubGrafo(arbol,graph,compararVertices)){ flag=1; }else{ flag=0; } return flag;}

Rodrigo Castro ReyesFacultad de Ingeniería Eléctrica y Computación

Page 20: Ejercicios_Resueltos_Grafos

La red telefónica del país enlaza las provincias, asignando un costo a las conexiones directas entre ellas. Unaconexión directa establece una relación bidireccional entre las dos provincias. Por ejemplo: Llamar de la provincia Xa la provincia Y cuesta $0.5 el minuto, y de la provincia Y a la provincia X, cuesta $0,9.Cada provincia esta identificada por un código único(numero entre 01 y 09). Por cada provincia, se maneja unconjunto de los números existentes en la misma.- Defina los TDAs que participan en este problema.- Escriba una función que dada la Red telefónica del país y dos números telefónicos retorne el costo mas bajo de lallamada por minuto, o -1 si no existe forma de comunicarse entre esos números. Recuerde explotar las funcionesdefinidas en la librería.Ejemplo:042387690 y 08909987Debería ubicar el costo mas bajo para comunicar a la provincia de código 04 con la de código 08. Ademas, esnecesario saber si el numero 2387690 pertenece a la provincia 04 y 909987 pertenece a la provincia 08. Supongaque el costo de llamar de la provincia del código 04 a la del código 08 sea $0.4 el minuto y llamar de la provincia delcódigo 08 a la del código 04 cuesta $0.9 el minuto. Su función debe retornar la tarifa con el costo mas bajo, este caso$0.4.Asuma que dispone de las siguientes funciones : int obtenerCodigo(int numero_telefono); que dado un numerosepara los dos primeros dígitos que representan el código de una provincia y int obtenerNumero(intnumero_telefono) que separa los dígitos que conforman el numero telefónico.

Solución:La red telefónica del país es un conjunto de provincias que están conectadas entre si. Si dos provincias estánconectadas esto supone que se podrán realizar llamadas desde la una a la otra. Esta red puede ser modelada como ungrafo no dirigido, con pesos definidos entre cada arco incidente en un par de vértices. Los pesos serán la tarifa querepresenta por llamar de una provincia a otra. Observe que definimos el grafo como no dirigido ya que el textomenciona que una conexión entre dos provincias siempre sera en ambos sentidos.Recordemos que en la librería del curso, se ha implementado los pesos de los arcos como un valor de tipo int. En esteejemplo se especifica que los pesos deben ser de tipo float. No nos fijaremos en este detalle, asumiremos que losarcos de la Red serán de tipo entero, así que nuestra función debe retornar un entero.Cada vértice del grafo es una provincia en la Red. Una provincia almacena como información un código que laidentifica , esto lo podemos representar como una variable de tipo int. Ademas la provincia tiene un conjunto denúmeros telefónicos existentes en ella, usaremos una lista enlazada que almacena enteros para modelar este campo.Para implementar la función que se nos pide, vamos a elaborar otras pequeñas funciones que nos hará resolver elproblema de forma mas fácil. El prototipo sera el siguiente: int costoMasBajo(Graph*RedTelefonica,int num_uno,intnum_dos);En ninguna parte del texto se nos pide construir la Red Telefónica, mucho menos generar la información de lasprovincias.Lo primero que debemos hacer es que a partir de los números recibidos por la función separar el código de laprovincia y el numero de teléfono. Esto lo haremos con la ayuda de las funciones que nos han proporcionado.Con el código obtenido debemos buscar a que provincia pertenece. Este trabajo lo realizara la función:GVertex*buscarProvinciaPorCodigo(Graph*RedTelefonica,int code)Cuando ya obtengamos la referencia a la provincia correcta, ahora debemos buscar que el numero de teléfonoingresado este en la lista de números almacenados en esa provincia. Si no se encuentra, retornaremos un mensajenotificando dicho evento. La funcion: NodeList *buscarNumeroDeProvincia(GVertex*vertice,int numero); resuelve elproblema descrito.Observe que la implementación de las funciones anteriores son muy parecidos a problemas que ya nos hemosencontrado en otros ejercicios.Teniendo ya las referencias a los vértices que almacenan las provincias implicadas, ahora buscaremos los dos arcos (iday vuelta) que las conectan. Esto es resuelto fácilmente por la función que disponemos en la librería: GEdge*graphGetLink(GVertex *source,GVertex *destination); que nos retorna el arco desde un vértice origen a un destino. Ahora debemos verificar cual de los dos arcos tiene el menor peso, este valor retornaremos.

Rodrigo Castro ReyesFacultad de Ingeniería Eléctrica y Computación

Page 21: Ejercicios_Resueltos_Grafos

Código:typedef struct{

List*num_existentes; int codigo;}Provincia;

GVertex*buscarProvinciaPorCodigo(Graph*RedTelefonica,int code){ NodeList*it=NULL; GVertex*vertex=NULL,*v=NULL; Provincia*p=NULL; for(it=listGetHeader(RedTelefonica);it!=NULL;it=nodeListGetNext(it)){ vertex=nodeListGetCont(it); p=(Provincia*)gVertexGetContent(vertex);//cast if(p->codigo==code){//si la provincia p tiene el mismo código que el que recibimos por parámetro entonces hemosencontrado el vertice objetivo v=vertex; break;//rompemos el lazo por que no hace falta seguir buscando } } return v;} //Hemos decidido implementar esta función para que usted tenga muy claro como se realiza este proceso debúsqueda de un elemento dentro de un conjunto de datos, el cual se denomina búsqueda secuencial, ya existe unafunción en la librería con el prototipo GVertex *graphSearchVertex(Graph *G, Generic cont, cmpfn cmp) que brinda lamisma funcionalidad de buscar un vértice dentro de un grafo.

NodeList *buscarNumeroDeProvincia(GVertex*vertice,int numero){ Provincia*provincia=(Provincia*)gVertexGetContent(vertice);//cast List*lista_numeros=provincia->num_existentes; NodeList*n=listSearch(lista_numeros,integerNew(numero),integerCmp);//busca numero en la lista de la provincia return n;}

int costoMasBajo(Graph*RedTelefonica,int num_uno,int num_dos){ int codigo=obtenerCodigo(num_uno); int numero=obtenerNumero(num_uno); int codigo_dos=obtenerCodigo(num_dos); int numero_dos=obtenerNumero(num_dos); GVertex*vertice_uno=buscarProvinciaPorCodigo(RedTelefonica,codigo); GVertex*vertice_dos=buscarProvinciaPorCodigo(RedTelefonica,codigo_dos); NodeList*n=buscarNumeroDeProvincia(vertice_uno,numero); NodeList*n_dos=buscarNumeroDeProvincia(vertice_dos,numero_dos); Gedge*arco_uno,*arco_dos; int peso_uno,peso_dos,min; if(n==NULL || n_dos==NULL){ printf("Numero Telefónico no encontrado"); return 0; }else{ arco_uno=graphGetLink(vertice_uno,vertice_dos); arco_dos=graphGetLink(vertice_dos,vertice_uno); if(arco_uno==NULL || arco_dos==NULL ){//si no existe conexión entre las dos provincias implicadas se retornara-1 return -1; }else{ peso_uno=arco_uno->weight; peso_dos=arco_dos->weight; min=peso_uno; if(peso_dos<peso_uno){ min=peso_dos; } } return min; }}

Rodrigo Castro ReyesFacultad de Ingeniería Eléctrica y Computación

Page 22: Ejercicios_Resueltos_Grafos

Tenemos un conjunto de páginas web que queremos poner en una página de enlaces. Pero nos damos cuenta deque algunas de estas páginas ya enlazan a otras del conjunto, de forma que podemos eliminar las ya enlazadas. Porejemplo, supongamos que la página 1 enlaza la 2 y la 3; y que la 3 enlaza a la 4. Si seleccionamos la 1, nonecesitamos poner 2, 3 ni 4. El objetivo es crear un método que, dado el conjunto de páginas (mismas que ya seencuentran enlazadas), retorne una lista de páginas tal que todas las páginas del conjunto estén enlazadas directa oindirectamente (El grafo original no debe ser modificado). Por ejemplo, dado el siguiente conjunto de páginas consus enlaces:

El conjunto resultante es {1,5}Solución:Vamos a definir el TDA PaginaWeb que va almacenar un campo entero llamado gradoConectividad que define elnumero de paginas al cual la pagina tiene un enlace directo e indirecto. Para calcular este numero lo que haremos es,seleccionar un vértice, efectuar un recorrido(Profundidad o Anchura) y contar el numero de vértices del recorrido (sintomar en cuenta el vértice inicio), este valor representa el numero de vértices a los cuales puedo llegar desde unorigen en particular, se hace esto para todos los vértices del grafo.Declaramos un conjunto inicialmente vació S, que podría ser representado como una lista de vértices.Ahora vamos a buscar en el grafo el vértice Vo con mayor conectividad y que no haya sido visitado, lo guardamos en elconjunto S, efectuamos nuevamente un recorrido(Profundidad o Anchura) desde Vo, esto con el objetivo de irencontrando las componentes conexas del grafo o los vértices óptimos para pertenecer al conjunto S . Hacemos estohasta que todos los vértices hayan sido visitados. Para el ejemplo de la descripción :

Vértice gradoConectividad

1 3

2 0

3 1

4 0

5 2

6 1

7 0

Al buscar un vértice con mayor grado de conectividad y no visitado encontraremos el 1. Aplicamos un recorrido y losvértices 2,3,4 quedan marcados como visitados. Entonces S={1}¿Existen vértices que aun no han sido visitados? Si entonces seguimos iterandoVolvemos a buscar un vértice con mayor grado de conectividad y no visitado, encontramos el 5. Aplicamos unrecorrido y los vértices 6,7 queda marcados como visitados. S={1,5}¿Existen vértices que aun no han sido visitados? No entonces nuestro algoritmo termina y retornamos el conjunto S,que dijimos que iba a ser representado como una lista de vértices.

Rodrigo Castro ReyesFacultad de Ingeniería Eléctrica y Computación

Page 23: Ejercicios_Resueltos_Grafos

Código:

typedef struct { int grado_conectividad; int id;} PaginaWeb;

PaginaWeb*paginaWebNew(int id ) { PaginaWeb*p = malloc(sizeof (PaginaWeb)); p->grado_conectividad = 0; p->id=id; return p;}

List* graphRecorridoAnchura(Graph*grafo, GVertex* source) { List*recorrido = listNew(); Queue*temporal = queueNew(); NodeList*it, *v; GVertex*vertice_actual; queueEnqueue(temporal, nodeListNew(source)); gVertexVisit(source); while (!queueIsEmpty(temporal)) { it = queueDequeue(temporal); vertice_actual = nodeListGetCont(it); listAddNode(recorrido, nodeListNew(vertice_actual)); for (v = listGetHeader(vertice_actual->LAdjacents); v != NULL; v = v->next) { GEdge*arco = nodeListGetCont(v); GVertex*vertice_adyacente = arco->destination; if (!gVertexIsVisited(vertice_adyacente)) { queueEnqueue(temporal, nodeListNew(vertice_adyacente)); gVertexVisit(vertice_adyacente); } } } return recorrido;}

void calcularGradoConectividad(Graph*grafo) { NodeList*it; GVertex*vertice_actual; PaginaWeb*pagina; List*recorrido; for (it = listGetHeader(grafo); it != NULL; it = nodeListGetNext(it)) { vertice_actual = nodeListGetCont(it); graphInit(grafo); recorrido = graphRecorridoAnchura(grafo, vertice_actual); pagina = gVertexGetContent(vertice_actual); pagina->grado_conectividad = listGetSize(recorrido) - 1; }}

Rodrigo Castro ReyesFacultad de Ingeniería Eléctrica y Computación

Page 24: Ejercicios_Resueltos_Grafos

int existenVerticesNoVisitados(Graph*grafo) { NodeList*it; GVertex*vertice_actual; for (it = listGetHeader(grafo); it != NULL; it = nodeListGetNext(it)) { vertice_actual = nodeListGetCont(it); if (!gVertexIsVisited(vertice_actual)) { return 1; } } return 0;}

GVertex*mayorGradoConectividad(Graph*grafo, cmpfn comparar) { NodeList*it, *maximo; GVertex*vertice_actual; List*vertices_no_visitados = listNew(); for (it = listGetHeader(grafo); it != NULL; it = nodeListGetNext(it)) { vertice_actual = nodeListGetCont(it); if (!gVertexIsVisited(vertice_actual)) { listAddNode(vertices_no_visitados, nodeListNew(vertice_actual)); } } vertice_actual = NULL; it = NULL; maximo = listGetHeader(vertices_no_visitados); for (it = listGetHeader(grafo); it != NULL; it = nodeListGetNext(it)) { if (comparar(it->cont, maximo->cont)) { maximo = it; } } return maximo->cont;}

int compararPaginas(Generic a,Generic b){ GVertex*vertice_uno,*vertice_dos; vertice_uno=(GVertex*)a; vertice_dos=(GVertex*)b; PaginaWeb*x,*y; x=vertice_uno->content; y=vertice_uno->content; if(x->grado_conectividad>y->grado_conectividad)return 1; return 0;}

List*conjuntoResultante(Graph*grafo) { NodeList*it; GVertex*vertice_actual; PaginaWeb*pagina; List*recorrido, *conjunto; graphInit(grafo); conjunto=listNew(); while (existenVerticesNoVisitados(grafo)) { vertice_actual = mayorGradoConectividad(grafo,compararPaginas); pagina=vertice_actual->content; listAddNode(conjunto, nodeListNew(vertice_actual)); recorrido = graphRecorridoAnchura(grafo, vertice_actual); recorrido = NULL; } return conjunto;}

Rodrigo Castro ReyesFacultad de Ingeniería Eléctrica y Computación

Page 25: Ejercicios_Resueltos_Grafos

Los geólogos del Instituto geofísico Ecuatoriano desean implementar un sistema de simulación de la erupción del volcán Tungurahua. Para ello se ha definido el siguiente mapa de las ciudades que están ubicadas en las quebradas o sitios por donde pasaría la lava, donde el punto T representa el volcán.

Con el objetivo de conocer el impacto de una erupción en la zona, se ha estimado en el mapa el porcentaje de lavaque pasaría por cada ciudad posiblemente afectada. Adicionalmente, se ha considerado que, dependiendo del áreade la ciudad afectada, cada ciudad retendría 100 toneladas por kilómetro cuadrado. Esto disminuiría la cantidad delava que podría llegar a las siguientes ciudades. La superficie en Km. cuadrados de cada ciudad es la siguiente: B=10, C=11, D=4, E=15, F=16, G=8, H=12, I=8, J=6, K=10, L=20, M=10. En ese caso, si el volcán generase 10.000 toneladas de lava, solo 2000 (el 20%) llegarían a la ciudadI, donde se retendrían 800 toneladas, quedando solo 1200 para afectar a las ciudades aledañas a I (K y J)a) Escriba la función calcularLava que dada la ciudad de origen y una cantidad X de lava generada por el volcán,calcule la cantidad de lava que llegaría a cada ciudad del mapa. b) Escriba la funcion lavaPorCiudad que dada una ciudad en especifico, muestre por pantalla la cantidad de lava quellega a dicha ciudad.

Solución:Vamos a representar al mapa como un grafo, en donde cada vértice almacena una ciudad en particular. Vamos adefinir el TDA Ciudad, que va almacenar la superficie de la ciudad, la cantidad de lava que llega y la cantidad de lavaque queda retenida, ademas de un entero identificador.Vamos a implementar la funcion solicitada: void calcularLava(GVertex*inicio, float cantidad_lava) de la siguienteforma:Vamos a iterar el conjunto de ciudades adyacentes a la ciudad origen. Partimos desde un vértice origen Vi querepresenta a la ciudad Ci . Para la primera ciudad cercana Ck ,calculamos la cantidad de lava que se retiene en esaciudad multiplicando la superficie con un factor de 100, tal como detalla la descripción del problema. Luegoobtenemos el peso del arco que une a ambas ciudades(Ci y Ck) lo transformamos en un porcentaje y lo multiplicamospor la cantidad de lava que recibimos por parámetro, este valor es la cantidad de lava que llega a esa ciudad(Ck).Ahora se resta la cantidad de lava que llega con la cantidad de lava que se retiene, este remanente es la nuevacantidad de lava que afectaría a las ciudades cercanas de Ck. En este momento llamamos de nuevo a la funcióncalcularLava pero ahora la ciudad de origen sera Ck y la cantidad de lava sera el remanente que acabamos de calcular,esto se hace siempre y cuando Ck tenga ciudades adyacentes que puedan ser afectadas por la lava. Luego de que setermine de afectar a las ciudades cercanas a Ck, se continua iterando con las ciudades cercanas a Ci. Al final de todaslas iteraciones habremos resuelto el problema.La implementación de la función lavaPorCiudad(GVertex*ciudad) no requiere de mucho esfuerzo, simplementetenemos que para una ciudad en particular mostrar por pantalla los resultados de los cálculos que efectuamos en lafunción calcularLava.

Rodrigo Castro ReyesFacultad de Ingeniería Eléctrica y Computación

Page 26: Ejercicios_Resueltos_Grafos

Código:typedef struct { char id; int superficie; float lava_retenida; float lava_entrante;} Ciudad;

Ciudad*ciudadNew(int i, int superficie) { Ciudad*c = malloc(sizeof (Ciudad)); c->id = i; c->lava_retenida = 0; c->superficie = superficie; return c;}

void calcularLava(GVertex*inicio, float cantidad_lava) { NodeList*it; float calculo; for (it = listGetHeader(inicio->LAdjacents); it != NULL; it = nodeListGetNext(it)) { GEdge*arco = nodeListGetCont(it); GVertex*vertice = (GVertex*) gEdgeGetDestination(arco); Ciudad *ciudad = (Ciudad*) gVertexGetContent(vertice); ciudad->lava_retenida = ciudad->superficie * 100; if (!listIsEmpty(vertice->LAdjacents)) { calculo = (( (float) gEdgeGetWeight(arco))/100) * cantidad_lava - ciudad->lava_retenida; ciudad->lava_entrante=calculo; calcularLava(vertice, calculo); } }}

void lavaPorCiudad(GVertex*ciudad) { Ciudad *c = (Ciudad*) gVertexGetContent(ciudad); printf("Ciudad %c llegan %.1f toneladas de lava y se retiene %.1f toneladas de lava",

c->id,c->lava_entrante ,c->lava_retenida);}

Rodrigo Castro ReyesFacultad de Ingeniería Eléctrica y Computación

Page 27: Ejercicios_Resueltos_Grafos

Un país X tiene tecnología pero no recursos naturales. Un país Y formado de N islas, tiene los recursos que Xnecesita. Entonces X planea invadir Y. Las fuerzas navales de X conocen las distancias entre las islas de Y y elporcentaje de fuerzas armadas de Y en cada isla. Derrotar a las fuerzas de Y supone una pérdida de soldados delpaís X, tanto durante el viaje como durante la invasión. La armada de X ha calculado que las bajas al ir de una isla Aa otra B responden a la siguiente ecuación: bajas(A,B)= distancia(A.B)*10 + 0.2*soldados_de_Y(B)

a) Defina un TDA que permita representar el despliegue del ejército Y antes de la invasión y la información táctica que posee el ejercito X.b) Escriba una función que para los ejércitos de X y Y. Calcule la mejor ruta para la invasión y el número de soldadosde X que sobreviven a los combates.

Solución:

Vamos a definir el TDA Isla, que tendrá un arreglo con las distancias al resto de islas del país y, el porcentaje desoldados del ejercito y un entero identificador.También definiremos el TDA EjercitoX que posee un grafo donde el contenido de sus vértices serán instancias del TDAIsla, existirá un arco entre cada par de islas en donde haya conexión, y el peso estará definido por la ecuación que nospresenta la descripción del problema, en conclusión este grafo representa al mapa del país y. En este TDA tambiéntendremos un campo entero que nos indicara la cantidad de soldados que posee el ejercito del país X. Observe que nose nos solicita generar el grafo, ni calcular el peso de cada uno de los arcos. El texto nos pide encontrar la mejor rutapara invadir el país y. ¿Como lo hacemos? ¿Que criterios usamos para definir tal ruta? Sabemos que lo que debemosoptimizar es el numero de bajas del ejercito x, es decir de todas las rutas que podríamos generar sobre el mapa delpaís y, cual es la que genera un menor numero de bajas. Dicho de otra forma, del grafo que representa al país y, cual esel subgrafo, que contenga todos los vértices , que genere el menor costo posible. Para encontrarlo vamos a usar elAlgoritmo de Prim, que nos retorna un árbol de costo mínimo de un grafo, pero necesitamos un vértice origen parapoder aplicar este algoritmo ¿Cual escogemos? Puede ser cualquiera, al final el peso del árbol resultante siempre serael menor de todos los que pudiesen generarse. El numero de bajas del ejercito x se calcula restando el peso del árbolde costo mínimo con la cantidad inicial de soldados.

Código:typedef struct{ int id; int porc_soldados; int *distancias;}Isla;

typedef struct{ Graph*ejercito_y; int cantidad_soldados; }EjercitoX;

Isla *islaNew(int id, int porc_soldados){ Isla*isla=malloc(sizeof(Isla)); isla->id=id; isla->porc_soldados=porc_soldados; return isla;}

Graph * AEM_Prim(Graph * G, GVertex *initial);

Rodrigo Castro ReyesFacultad de Ingeniería Eléctrica y Computación

Page 28: Ejercicios_Resueltos_Grafos

Graph*calcularRutaInvasion(EjercitoX*ejercito){ NodeList*p=listGetHeader(ejercito->ejercito_y); GVertex*inicio=nodeListGetCont(p); Graph*arbol_costo_minimo=AEM_Prim(ejercito->ejercito_y,inicio); return arbol_costo_minimo;}

int calcularCosto(Graph*grafo){ NodeList*it,*v; GVertex*vertice; int costo=0; GEdge*arco; for(it=listGetHeader(grafo);it!=NULL;it=nodeListGetCont(it)){ vertice=nodeListGetCont(it); for(v=listGetHeader(vertice->LAdjacents);v!=NULL;v=nodeListGetNext(v)){ arco=nodeListGetCont(v); costo=arco->weight+costo; } } return costo;}

int calcularBajas(EjercitoX*ejercito){ Graph*ruta=calcularRutaInvasion(ejercito); int costo=calcularCosto(ruta); return ejercito->cantidad_soldados-costo;}

Rodrigo Castro ReyesFacultad de Ingeniería Eléctrica y Computación