Sei sulla pagina 1di 12

Alumno: Juan Ramón Astorga Castaños.

Matricula: 83225 Grupo: K039

Materia: Algoritmos y estructura de datos.

Docente: Mtra. Lucia González Cabrera

Actividad de aprendizaje #4: Teoría de grafos.

Culiacán, Sinaloa 02/10/2017


Instrucciones:

Resuelve el siguiente problema:

La figura muestra un mapa con 4 distritos A, B, C y D. Se trata de pintar cada distrito con
un color de forma que, dos regiones con un borde común (que no sea un punto) tengan
distintos colores y queremos hacer esto usando un mínimo de colores.

1. Encuentra una representación en términos de vértices y aristas de un grafo a partir del


mapa dado.

2. Investiga un algoritmo que aplicado a grafos te permita ir coloreando los vértices de tal
forma que no coincidan en color, con el color de los vértices que estén unidos a ellos a
través de aristas.

3. Como resultado presenta un documento en formato Word que ofrezca la explicación del
algoritmo de coloración que hayas utilizado, en conjunto con la corrida a mano de la
coloración del grafo, la cual representa al mapa dado en la actividad.
Hay por lo menos dos maneras evidentes de representar un grafo en un computador,
utilizando la notación pseudoformal propuesta en [2]. La primera utiliza lo que se conoce
como una matriz de adyacencia, a saber:

Un valor verdadero en la posición (i,j) de la matriz indica que hay una arista que conecta
al nodo i con el nodo j. Para representar un grafo pesado se puede cambiar la matriz de
adyacencia de lógicos a una matriz de registros, siendo el peso un campo del registro.

Esta representación es muy útil cuando se cumplen dos condiciones principales. Primero,
si se conoce el número exacto de nodos en el grafo no hace falta utilizar estructuras
dinámicas porque las estáticas se pueden acceder con mayor facilidad. Segundo, la
matriz de adyacencia no contiene un gran número de elementos en FALSO.
Sin embargo, puede que esta solución no sea la más conveniente por el hecho de que la
matriz esparcida ocupará más memoria que su contraparte estática a medida que el
número de conexiones entre los nodos sea mayor, ya que habrá pocos elementos no
nulos dentro de la matriz. En ese caso, se debe utilizar otra implementación.

Esta representación ocupa menos memoria que la anterior sin importar si la matriz
esparcida está muy llena o no, pero puede incrementar la complejidad de algunas
operaciones, como por ejemplo saber cuáles nodos son adyacentes a un nodo en
particular (en caso de un grafo dirigido).

De nuevo, si las aristas también tienen algún tipo de información asociada bastaría una
leve modificación en la clase Ady estructura para agregarla.

Por supuesto, se deben implementar las operaciones más comunes como agregar un
nodo al grafo, eliminar un nodo del grafo y conectar un nodo con otro. En adición, se
puede implementar todo tipo de operaciones basadas en la teoría de grafos, para resolver
los problemas que se describen a continuación.
Hoy en día, la aplicación de la computación es requerida en la mayoría de los campos
científicos y la teoría de grafos no es la excepción. Por ello, también estableceremos las
nociones básicas de su representación computacional y mostraremos algunos algoritmos
y problemas clásicos en grafos.

Si vemos el mapa de una ciudad, sus calles y puntos sobresalientes puede ser
representados a través de líneas (las calles) y puntos (lugares relevantes). Por ejemplo:

Representación en términos de grafos de calles y sitios.


Representación en términos de grafos de calles y sitios.

En nuestro ejemplo inicial, los vértices son cuatro: "mi casa", "tíos", "tienda", "escuela".
Además tenemos 5 aristas que podemos identificar como los caminos:

 mi casa - escuela
 mi casa - tienda
 mi casa - tíos
 escuela - tíos
 tienda – tíos

Antes de seguir, aclaremos que la cardinalidad de un conjunto (en palabras sencillas) es


el número de elementos que tiene el conjunto en cuestión. Para nuestro mapa, hemos
creado un grafo cuya cardinalidad (número) de su conjunto de vértices (los puntos
relevantes) es 4 mientras que la cardinalidad del conjunto de aristas (los caminos entre
los puntos relevantes) es de 5. Por otro lado, para poder indicar de qué aristas hablamos,
basta con decir cuáles son los extremos que tienen dicha aristas. En ocasiones, dos o
más aristas tienen el mismo par de vértices por extremo. Lo cual puede representar dos o
más caminos diferentes en un mapa o diferente tipo de relaciones entre individuos.
Aunque en la mayoría de los problemas olímpicos no hay grafos con aristas dobles, es
necesario tener una forma de identificar de cual arista hablamos si se presenta el caso.
Por tanto, hagamos la siguiente notación para poder referirnos a las aristas de manera
formal.

El valor ponderado de las aristas por lo común es un conjunto de valores constantes. Sin
embargo, es posible que los valores ponderados cambien conforme alguna función dada.
En general, nosotros trabajaremos con grafos con valores ponderados constantes pero no
olvidemos que estos valores son más que valores constantes. Un ejemplo sencillo: un
camión que recolecta basura a lo largo de la calle; entre más basura recoge, más es el
costo de viajer entre calle y calle (debido a que tiene más carga que transportar). Por
tanto, el valor ponderado de una calle depende de qué tan cargado pase el camión y no
es un valor constante.

Grafo G(V,A) con 6 vértices y 8 aristas con sus respectivos valores ponderados del
conjunto W

En la anterior figura tenemos un grafo G(V,A), con el conjunto de vértices:


V = { v1, v2 , v3, v4, v5, v6 }
el conjunto de valores ponderados W:
W = { α1, α2, α3, α4, α5, α6, α7, α8}.
y el conjunto de aristas como:
A = {A(1,2), A(1,5), A1(2,3), A2(2,3), A(3,4), A(4,5), A(4,6)}.

Es de manera natural referirse como vértices adyacentes, a dos vértices que están
relacionados (conectados) por una arista. Análogamente aristas adyacentes son dos
aristas aij = A(vi,vj) y akl = A(vk,vl), que tienen un extremo en común. Es
decir, vi = vk ó vi= vl ó vj = vk ó vj = vl.

En un mapa, los caminos no siempre se consideran para ambas direcciones, ya que


podemos encontrar calles donde el flujo es sólo en un sentido. Por lo tanto, requerimos
que un grafo tenga aristas cuya dirección de flujo tenga un solo sentido. Cuando una
arista no tiene sentido, se llama arista bidireccionada. De igual modo, cuando las aristas
tienen asignado un sentido, se llaman aristas direccionadas. Normalmente, al arista
A(i , j) se considera igual a la arista A(j , i) cuando se trata de aristas bidireccionadas pero
se consideran diferentes cuando son aristas direccionadas. Formalizando las definiciones:

Representación computacional

Ahora que sabemos qué es un grafo. Veremos cómo podemos expresarlo en términos de
programación. Matemáticamente, la representación de un grafo G(V,A) es a través de una
matriz M, de n x n con lo cual representamos el valor ponderado de la aristas aij en la
entrada (i,j) de la matriz M. Sin embargo, antes de ver grafos ponderados, veamos como
representamos grafos cuyas aristas no son ponderadas.

Nota: Cuando hablamos de la entrada (i,j) de una matriz M nos referimos al valor, de la
matriz, que se encuentra en el renglón i y columna j.

Volviendo por n-esima ocasión a nuestro ejemplo del mapa. Interpretemos nuestro grafo
como una matriz de ceros y unos. Donde un cero en la posición (i,j) representa que hay
camino del vértice i al vértice j y un uno, que si hay camino. Entonces, nuestra matriz
quedaría:
Como en nuestro ejemplo los caminos son bidireccionados, entonces la matriz es
simétrica (Si trazas una línea desde la esquina superior izquierda hasta la esquina inferior
derecha, verás la simetría). Es decir, si la entrada (i,j) toma el valor de cero o uno,
entonces también la entrada (j,i).

En programación, una matriz se representa como un arreglo bidimensional. Por lo tanto,


imaginemos que tenemos un archivo que contiene con las características mencionadas.

El archivo input contiene, en su primera línea, un entero 0 < n < 1000 que representa el
número de vértices. Seguidamente vienen n líneas con n enteros que representa a la
matriz de un grafo. Un uno en el renglón i, columna j, representa que hay arista del
vértice i al vértice j y un cero representa que no hay arista.

4
0 1 1 1
1 0 0 1
1 0 0 1
1 1 1 0

El código para leer este formato de grafo quedaría como el siguiente:

1 #include "stdio.h"
2 int main(void)
3 {
4 int i,j,n,**matriz;
5 FILE *input;
6
7 // Intentamos abrir el archivo. Terminamos en caso de
8 // existir algún problema.
9 if( (input=fopen("input_1_2_1.txt","r+t")) == NULL )
10 return 0;
11
12 // Leemos la cantidad de vértices.
13 fscanf(input,"%d",&n);
14
15 // Creamos los renglones.
16 matriz = new int*[n];
17 for(i = 0; i < n; i++)
18 {
19 // Creamos las columnas del renglón i y leemos.
20 matriz[i] = new int[n];
21 for(j = 0; j < n; j++)
22 fscanf(input,"%d",&matriz[i][j]);
23 }
24 // Cerramos el archivo.
25 fclose(input);
26
27 // Liberamos la memoria dinámica pedida.
28 for(i = 0; i < n; i++)
29 delete[] matriz[i];
30 delete[] matriz;
31
32 return 0;
33 }

El archivo input contiene, en su primera línea, dos enteros <0 < n < 1000 y k que
representan el numero de vértices y aristas respectivamente. Seguidamente
vienen k líneas con 2 enteros cada una y que representan a las k aristas del grafo. Los
enteros v y u, de cada línea, representan los extremos de la arista en cuestión. Los
vértices son enumerados a partir de cero.

5 7
0 1
0 2
1 2
1 3
2 3
2 4
3 4

Sirve tanto para grafos bidireccionados como para grafos direccionados. La única
diferencia radica al momento de leer los datos.

1 #include "stdio.h"
2 int main(void)
3 {
4 int i,j,n,k,u,v,**matriz;
5 FILE *input;
6
7 // Intentamos abrir el archivo. Terminamos en caso de
8 // existir algún problema.
9 if( (input=fopen("input_1_2_3.txt","r+t")) == NULL )
10 return 0;
11
12 // Leemos la cantidad de vértices.
13 fscanf(input,"%d %d",&n,&k);
14
15 // Creamos los renglones.
16 matriz = new int*[n];
17 for(i = 0; i < n; i++)
18 {
19 // Creamos las columnas del renglón i e
20 inicializamos las aristas.
21 matriz[i] = new int[n];
22 for(j = 0; j < n; j++)
23 matriz[i][j]=0;
24 }
25
26 for(i = 0; i < k; i++)
27 {
28 // Leemos las k líneas y asignamos el valor de uno a
29 las aristas
30 // correspondientes en ambas direcciones.
31 fscanf(input,"%d %d",&u,&v);
32 // marcamos la arista de u a v
33 matriz[u][v] = 1;
34 // En el caso de un grafo direccionado, la siguiente
35 línea se omite
36 // ya que marca la arista de v a u
37 matriz[v][u] = 1;
38 }
39
40 // Cerramos el archivo.
41 fclose(input);
42
43 // Liberamos la memoria dinámica pedida.
44 for(i = 0; i < n; i++)
45 delete[] matriz[i];
46 delete[] matriz;

return 0;
}

Una sola línea con la lista de los vértices aislados. La lista debe estar ordenada de menor
a mayor y separados por un espacio.

Input.txt output.txt
8 7 2 6 7
0 1
0 4
1 3
1 4
3 4
3 5
4 5

Para resolverlo, hagamos una lista de todos los vértices disponibles (n en total). La lista la
inicializamos con ceros considerando que un cero equivale a que el vértice es aislado. A
continuación, leemos las aristas y por cada arista marcamos con el valor de 1, en nuestra
lista, a los vértices que forman dicha arista. De este modo, marcaremos con un uno (en la
lista) a todos los vértices que son extremos de al menos una arista. Por lo tanto, al final
solo imprimimos los vértices que permanecieron con cero en la lista.
#include "stdio.h"
int main(void)
1
{
2
int i,n,k,u,v,*lista;
3
FILE *input,*output;
4
5
// Intentamos abrir el archivo. Terminamos en caso de
6
// existir algún problema.
7
if( (input=fopen("input_1_2_4.txt","r+t")) == NULL )
8
return 0;
9
10
// Leemos la cantidad de vértices.
11
fscanf(input,"%d %d",&n,&k);
12
13
// Inicializamos nuestra lista de vértices como
14
aislados.
15
lista = new int[n];
16
for(i = 0; i < n; i++)
17
lista[i] = 0;
18
19
for(i = 0; i < k; i++)
20
{
21
// Leemos las k líneas y marcamos como vértices no
22
aislados a u y v.
23
fscanf(input,"%d %d",&u,&v);
24
// marcamos la arista de u a v
25
lista[u] = 1;
26
lista[v] = 1;
27
}
28
29
// Cerramos el archivo.
30
fclose(input);
31
32
// Abrimos en modo de escritura el archivo de salida.
33
output=fopen("output.txt","w+t");
34
35
// búscamos los vértices aislados (marcados como
36
ceros)
37
for(i = 0; i < n; i++)
38
{
39
if( lista[i] == 0 )
40
fprintf(output,"%d ",i);
41
}
42
43
// Cerramos el archivo de salida.
44
fclose(output);
45
46
// Liberamos la memoria dinámica pedida.
47
delete[] lista;
48
49
return 0;
}
Bibliografía.

Nuñez, A. (2010). Introducción a la Teoría de Grafos en Matemática computacional,. Obtenido de


IEU: http://l2.ieu.edu.mx/mod/folder/view.php?id=119811

Guti´errez, F. J. (Octubre de 2004). Apuntes de Matem´atica Discreta, Grafos. Obtenido de UCA:


http://www2.uca.es/matematicas/Docencia/ESI/1711003/Apuntes/Leccion14.pdf

Potrebbero piacerti anche