Free Web Site - Free Web Space and Site Hosting - Web Hosting - Internet Store and Ecommerce Solution Provider - High Speed Internet
Search the Web

Centro de un grafo


Este probema consiste en que dado un conjunto finito de vertices y arcos, se determine el vertice de menor excentricidad, o en terminos reales el vertice "centro de operaciones" del cual se minimizan los costos de transporte.

Ejemplo :

Se quiere establecer el centro de operaciones para la línea AeroPerú. Los ciudades candidatos son Santiago, Miami y Lima. Después de hacer un estudio de costos se llegó a la siguiente situación :

Destinos :

V1 : Miami
V2 : Santiago
V3 : Lima

Costos :

Miami - Santiago : 8 millones
Miami - Lima : 6 millones
Santiago - Lima : 2 millones

Una de las técnicas existente, es llevar los costos asociados de cada camino a una matriz, en donde la posición (fila,columna), representa el vertice inicial y final respectivamente.

El arco que representa el camino Miami - Santiago (v1 -> v2) se identifica en la matriz como :

matriz[0][1] = 8

, ya que

fila = 0 : vertice inicial (V1)
columna = 1 : vertice final (V2)
costo = 8

Como el grafo no es dirigido, o sea los caminos se dan en ambos sentidos, el viaje de Santiago a Miami (v2 -> v1) se representa en la matriz como : matriz[1][0] = 8

Se puede intuir que la matriz de costos es de tipo NxN, donde N : numero de vertices.

Tabla de costos

--- 8 6
8 --- 2
6 2 ---

A partir de esta tabla de costos, se hacen una serie de operaciones matemáticas, con el propósito de encontrar el centro del grafo.

Representación de un arco

Para almacenar la información correspondiente a los arcos, creamos una estructura con los siguientes campos :

vi : vertice inicial del arco --> 0 ... número de vertices - 1
vf : vertice final del arco --> 0 ... número de vertices - 1
c : costo asociado

typedef struct
{
   int vi , vf;
   int c;
} T_ARCO , *ARCO;

Teniendo la estructura , definimos tres funciones para esta estructura :

1 ) crear arco : reservar memoria dinamica para almacenar una variable de tipo T_ARCO, además de inicializar los campos.

ARCO crear_arco(int vi, int vf, int costo)
{
   ARCO aux = NULL;
   aux = (ARCO) malloc(sizeof(T_ARCO));
   aux->vi = vi;
   aux->vf = vf;
   aux->c = costo;
   return aux;
}

2 ) crear arreglo de arcos : reservar memoria dinámica para crear un arreglo de tamaño num_arcos (numero de arcos) de tipo ARCO. Dado que el tipo ARCO es un puntero, lo que se esta haciendo es crear un arreglo de punteros.

ARCO *crear_arr_arcos(int num_arcos)
{
   ARCO *aux = NULL;
   aux = (ARCO *) calloc(num_arcos,sizeof(ARCO)); 
   return aux; 
} 

En un grafo no dirigido, un camino de dos sentidos, se considera como un sólo arco (según esta implementación).

3 ) destruir arcos : liberar memoria para el arreglo de arcos.

void destruir_arcos(ARCO *arr, int num_arcos)
{
   for(int i = 0; i < num_arcos; i++) free(arr[i]);
   free(arr);
}

Las funciones que acabamos de implementar , nos sirven para leer la información de cada arco, según el siguiente seudo-programa :

int main()
{
   :
   :
   < Leer num_arcos ... >
   ARCO *arr_arcos = crear_arr_arcos(num_arcos);
  
   for(int i = 0; i < num_arcos; i++)
   {
        < Leer vi,vf,costo ... >
        arr_arcos[i] = crear_arco(vi,vf,costo);
   }

   < RESTO DEL PROGRAMA ... >

   destruir_arcos(arr_arcos);
   :
   :
   return 0;
}         

Matriz de costos

La dimensión o tamaño de la matriz de costos está determinada por el número de vertices del grafo, por lo tanto podemos crear una función que cree un matriz dinámica de tipo int.

int **crear_matriz_costos(int num_vertices)
{
   int **aux = NULL;
   aux = (int **) calloc(num_vertices,sizeof(int *));
   for(int i = 0; i < num_vertices; i++) 
      aux[i] = (int *) calloc(num_vertices,sizeof(int));
   return aux;
}

Ya que implementamos la reserva de memoria, creamos una función que libere memoria.

void destruir_matriz_costos(int **matriz, int num_vertices)
{
   for(int i = 0; i < num_vertices; i++) free(matriz[i]);
   free(matriz);
}
Ejemplo :

    int **matCostos = crear_matriz_costos(5);
    :
    :
    destruir_matriz_costos(matCostos,5);

Algoritmo de Floyd

Este algoritmo se puede describir en los siguientes pasos :

1 ) Inicializar una matriz de costos "infinitos". Esto indica que no existe arco entre vertices o que el costo de ir de un vertice a otro es demasiado alto.

Si se esta trabajando con costos del orden de : 1....30 , una constante adecuada para representar este elevado costo, puede ser 2000. Por lo tanto en el programa se puede definir :

#define NO_EXISTE_ARCO 2000

Para inicializar la matriz con este valor, podemos implementar la siguiente función :

void inic_matriz_costos(int **mat_costos , int num_vertices)
{
   for(int i = 0; i < num_vertices; i++)
      for(int j = 0; j < num_vertices; j++)
         mat_costos[i][j] = NO_EXISTE_ARCO;
}

2 ) Pasar la información del arreglo de arcos, a la matriz. Lo que se quiere es deteminar en que posición de la matriz, va a ir el costo asociado a cada arco. Además, si el grafo es no dirigigo, con la información de un arco, asignamos dos costos, en forma simétrica.

Parametros : int **mat_costos , ARCO *arr, int num_arcos , int dirigido

vertice inicial --> fila =  arr[i]->vi;
vertice final --> columna = arr[i]->vf;
costo --> mat_costos[fila][columna] = arr[i]->c;

SI (NO dirigido) ENTONCES 
   mat_costos[columna][fila] = arr[i]->c;

Función :
void asignar_costos(int **mat_costos, ARCO *arr , int num_arcos, int dirigido)
{
   int i,f,c;
   for(i = 0; i < num_arcos; i++)
   {
      f = arr[i]->vi;
      c = arr[i]->vf;
      mat_costos[f][c] = arr[i]->c;
      if (!dirigido) mat_costos[c][f] = arr[i]->c;
   }
}

3 ) Evaluar la matriz de costos , en la función floyd( ) que realiza los siguientes pasos :

a) Asigna 0 a la diagonal principal
b) Busca la excentricidad de cada vertice
c) Determina un arreglo con los mayores valores de cada columna.
d) Busca el menor elemento del arreglo
e) Retorna la posición de ese elemento

La posición retornada corresponde al vertice con menor excentricidad o centro del grafo.

int floyd(int **matriz, int num_vertices)
{
   int i,j,k;
   
   for (i = 0; i < num_vertices; i++) matriz[i][i] = 0;

   int suma;
   for(k = 0; k < num_vertices; k++)
  {
       for(i = 0; i < num_vertices; i++)
         for(j = 0; j < num_vertices; j++)
         {
            suma = matriz[i][k] + matriz[k][j];
            if ( suma < matriz[i][j] ) matriz[i][j] = suma;
         }
   }
   
    int *arreglo_aux = (int *) calloc(num_vertices,sizeof(int));

    int max_col;
    for (i = 0 ; i < num_vertices;i++)
    {
        max_col = -12000;

        for(k = 0 ; k < num_vertices; k++)
        if (matriz [i][k] > max_col)
            if(matriz[i][k] != NO_EXISTE_ARCO) 
                  max_col = matriz[i][k];
        arreglo_aux[i] = max_col;
    }

    int posicion;
    int menor = 20000;
    printf("\n \n Arreglo con los maximos ");
    for (k = 0; k < num_vertices;k++)
    {
        printf(" %d ",arreglo_aux[k]);
        if (arreglo_aux[k] != 0)
            if ( arreglo_aux[k] < menor )
            {
                menor = arreglo_aux[k];
                posicion = k;
            }
    }

    free(arreglo_aux);
    return posicion;
} 

ALGORITMO DEL PROGRAMA PRINCIPAL

Leer num_arcos, num_vertices
Leer dirigido (1 = si / 0 = no)

Crear y leer el arreglo de arcos

Crear la matriz de costos
Inicializar la matriz
Asignar costos del arreglo de arcos a la matriz
Aplicar floyd

Imprimir el vertice resultante
Liberar memoria

-------------