Challenge of the Ant Colony

Asked

Viewed 1,063 times

29

I have a programming marathon problem and I want to know if my solution is right, as well as improvement suggestions.

Problem

A group of ants are really proud because they have built a magnificent and great colony. However, the enormous size has become a problem, because many ants do not know the way between some parts of the colony. They desperately need your help!

The ant colony was made as a series of N tingling, linked by tunnels. The ants, obsessive as they are, counted the tingling sequentially as they built them.

The first anthill, numbered 0, does not require any tunnel, but for each of the subsequent anthills, 1 to N-1, the ants also built a single tunnel that connected the new anthill to one of the existing anthills.

Of course, this tunnel was enough to allow any ant to go to any previously constructed anthill, possibly passing through other anthills in its path, but they did not bother to make extra tunnels and continued to build more anthills.

His work is, given the colony structure and a set of queries, to calculate for each query the shortest path between pairs of anthills. The length of a path is the sum of the lengths of all tunnels that need to be explored.

Entree

Each test case extends over several lines. The first line contains an integer n represents the number of anthills in the colony (2 ≤ N ≤ 105).

Each of the following lines N-1 contains two integers describing a tunnel. Line i, for 1 ≤ i ≤ N-1, contains Ai and Li, indicating that the ant hill i was connected directly to the ant hill Ai with a tunnel length Li (0 ≤ ≤ Ai i-1 e 1 ≤ Li ≤ 109).

The next line contains an integer Q representing the number of consultations below (1 Q 105).

Each of the next Q lines describes a query and contains two integers S and T distinct (0 ≤ S, T ≤n -1), representing, respectively, the origin and destination anthills.

The last test case is followed by a line containing a zero.

Exit

For each case the test output with a single line Q integer numbers, each of them being the length of a shorter path between the ant pair of a query. Write the results for each command in the same order as the queries in the input.

My current solution:

int main()
{
int formigueiros[100000], n=1, distancia[99999], usuario[3], usuario[3], S, T, situacoes, n3=1, analise[3],n5=0;
int *n2=&usuario[0];
int *n4=&analise[0];
int *valordis=&distancia[0];
int *valorfor=&formigueiros[1];
printf("digite a quantidade de formigueiros(minimo 2 e maximo 100000)");
scanf("%d",&formigueiros);
while(n<formigueiros)
{

    printf("digite em qual formigueiro o %d# esta conectado e a distancia(de 1 a 1000000000) entre eles", formigueiros);
    scanf("%d", &usuario);
    *valorfor=n2;
    n2++;
    n2++;
    *valordis=n2;
    valordis++;
    valorfor++;
    n++;

}
printf("quantas situações deseja analisar?(de 1 a 100000)");
scanf("%d",&situacoes);
while(n3<situações)
{
    printf("digite a origem S e o destino T");
    scanf("%d", &analise);
    S=n4;
    n4++;
    n4++;
    T=n4;
    int novasoma=0;
    if(S>1)
    {
        S--;
        int *pont1=&formigueiros[1];
        int *pont2=&distancia[0];
        while(n5<S)
        {
            pont1++;
            pont2++;
            n5++;
            if(n5==S && *pont1>0)
            {
                int dis=*pont2
                n5=*pont1;
            }
        }
        novasoma=dis+novasoma
    }
    if(T>1)
    {
        n5=1;
        *pont1=&formigueiros[1];
        while(n5<T)
        {
            pont1++;
            n5++;
        }
    }

}
}
  • I don’t know how to fix this. I’ve been told that it’s using graph theory, which is using mathematical equations,.

  • Seems to me the problem with wanderer, an np-hard optimization problem. Here has an interesting material about.

2 answers

18

By the text of the problem, I’m guessing the origin is this site:
http://br.spoj.com/problems/ANTS10/

As the comments:

of @Abriel:

is a problem involving graph theory

and @Eric-silva:

It seems to me the traveling cacheiro problem, an np-hard optimization problem (or TSP - Travelling Salesman Problem in english).

Yes, it is a graph problem, but although "it seems", this problem is not NP-hard and also is not NP, because in the statement, the author specifies that he has a solution (it is decidable):

this tunnel was enough to allow any ant to any previously constructed anthill...

and there is no steering restriction in the tunnels (not a directed graph).

In fact, at first glance, it seems to be a problem Tree of Minimum Extension, that can be solved in polynomial time (P).

Explaining in a very simple and informal way, computational problems are divided into classes.

For further reference:
https://en.wikipedia.org/wiki/Computational_complexity

The three classes mentioned here are:

P ==> Can be solved in polynomial time

NP ==> They cannot be solved in polynomial time (but can be checked in polynomial time)

NP-hard ==> Are problems equivalent to the most difficult problems in NP and eventually may not even have solution

Examples of time variation depending on the size of the input:

Given an input of N elements:

A problem P complex O(N^2) (for each element added to the input, time increases squared):

N=2 -> demora 4 segundos
N=20 -> demora 400 segundos
N=200 -> demora 40.000 segundos

A problem NP intricate O(2^N) (for each element added to the input, the time increases 2 high to the input):

N=2 -> demora 4 segundos
N=20 -> demora 1.048.576 segundos
N=200 -> demora 1.606.938.044.258.990.275.541.962.092.341.162.602.522.202.993.782.792.835.301.376 segundos

The above examples are fictitious and have no relation to the problem dealt with here.

The problem "Minimum Extension Tree" is important and has practical utility, for example, in calculating the best route in a GPS or, when buying an air ticket, find better route between origin and destination.

Analyzing

  • Each anthill is interconnected by a tunnel and there is a cost to travel it.

  • There is always at least 1 path from one ant hill to another.

  • The total cost of navigating from one anthill to another is the sum of the each tunnel travelled.

Entree

As the site mentioned at the beginning of the reply, I am assuming that the data will be read from a file and not typed by the user, therefore the function scanf may not be ideal in this case.

Input file format (extracted from response start site):

6              <=== Número de formigueiros = 6
0 8            <=== Linha 1: túnel do formigueiro F1 até F0, com custo 8
1 7            <=== Linha 2: túnel do formigueiro F2 até F1, com custo 7
1 9            <=== Linha 3: túnel do formigueiro F3 até F1, com custo 9
0 3            <=== Linha 4: túnel do formigueiro F4 até F0, com custo 3
4 2            <=== Linha 5: túnel do formigueiro F5 até F4, com custo 2
4              <=== Número de consultas = 4
2 3            <=== Consulta caminho de F2 a F3
5 2            <=== Consulta caminho de F5 a F2
1 4            <=== Consulta caminho de F1 a F4
0 3            <=== Consulta caminho de F0 a F3
2              <=== Número de consultas = 2
0 1            <=== Consulta caminho de F0 a F1
2              <=== Número de consultas = 2
1 0            <=== Consulta caminho de F1 a F0
0 1            <=== Consulta caminho de F0 a F1
6              <=== Número de consultas = 6
0 1000000000   <=== Consulta caminho de F0 a F1000000000
1 1000000000   <=== Consulta caminho de F1 a F1000000000
2 1000000000   <=== Consulta caminho de F2 a F1000000000
3 1000000000   <=== Consulta caminho de F3 a F1000000000
4 1000000000   <=== Consulta caminho de F4 a F1000000000
1              <=== Número de consultas = 1
5 0            <=== Consulta caminho de F5 a F0
0              <=== Fim do arquivo

Data Structure for Storage

Two possibilities to use for storing the graph are:

1) Adjacency matrix

2) Adjacency list

Algorithms

There are several known algorithms to handle the "Minimum Extension Tree" problem, among them:

Borůvka’s Algorithm (developed in 1926) https://en.wikipedia.org/wiki/Borůvka's_algorithm)

Algorithm by Dijkstra
https://en.wikipedia.org/wiki/Dijkstra's_algorithm

Prim’s algorithm
https://en.wikipedia.org/wiki/Prim's_algorithm

Kruskal’s algorithm
https://en.wikipedia.org/wiki/Kruskal's_algorithm

Exit

The cost of the smallest path between 2 anthills, for each query.

Implementation

Below is a commented example of implementation that does not solve the problem, but finds its way between the two anthills only for the response start link file data.

Going through a graph to find the best path usually requires auxiliary data structures, for example, batteries or queues.

In the example below, the structure used was a stack.

As it is a marathon or competition, I chose to demonstrate a possible implementation only on a didactic basis, therefore:

  • it is not optimized and will probably not find exactly the smallest path for all possible cases

  • it does not predict errors in the input data

  • it does not provide for alternative paths or cycles

Marathon or Contest

Since it is a marathon, probably the program will be tested in as many ways as possible to check how it treats errors in reading the data, or even in the logic of this data.

Two clear examples of this (taken from the site at the beginning of the answer) are:

  • the second and fourth consultations indicate that there will be 2 and 6 consultations respectively, but only 1 and 5 (consultations).

  • the fourth query asks for a way to the ant hill F1000000000, however, it does not exist

Also, carefully observing the data input structure described in the problem:

Each of the following lines N-1 contains two integers describing a tunnel. Line i, for 1 i N-1, contains Ai and Li, indicating that the Anthill i was connected directly to the anthill Ai with a tunnel length Li (0 Ai i-1 and 1 Li 109).

  • Probably (I didn’t test), this problem can be solved with a simple tree, without the need to use a "Minimum Extension Tree".

Follow the example:

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

// Estrutura de controle da pilha
typedef struct tpilha {
    int item;
    struct tpilha *prev;
} pilha;

// Variável que armazena a pilha
static pilha *inicio_pilha = NULL;

// PUSH - inclui um elemento na pilha
void push(int elemento)
{
    pilha *temp;
    // Cria um elemento temporário
    temp = (pilha *) malloc(sizeof(pilha));
    temp->item = elemento;
    temp->prev = NULL;

    if (inicio_pilha == NULL) {
        // Define como início da pilha se esta estiver vazia
        inicio_pilha = temp;
    } else {
        // Inclui um elemento no topo da pilha
        temp->prev = inicio_pilha;
        inicio_pilha = temp;
    }
    return;
}

// POP - Retira um elemento da pilha ou dá erro, caso a pilha esteja vazia
int pop()
{
    pilha *temp;
    int ret;

    if (inicio_pilha == NULL) {
        perror("Erro fatal: pilha vazia.");
        exit(1);
    } else {
        temp = inicio_pilha;
        ret = temp->item;
        inicio_pilha = inicio_pilha->prev;
        free(temp);
    }
    return ret;
}

// Verifica se a pilha está vazia
int pilhavazia()
{
    return (inicio_pilha == NULL ? 1 : 0);
}

// Encontra um caminho entre os parâmetros inicio e fim
int encontra_caminho(size_t tamanho, int **f, int inicio, int fim)
{
    int i, j;
    int custo[tamanho];
    int visitados[tamanho];
    int contador = 0;
    int elemento_atual;
    int custo_atual = 0;
    int elemento_ja_visitado;

    // Inicializa os vetores
    for (i=0; i<tamanho; i++) {
        custo[i] = INT_MAX;
        visitados[i] = -1;
    };
    // Empilha um custo inicial 0 e o formigueiro de início
    push(custo_atual);
    push(inicio);

    // Looping de busca
    for(;;) {
        // se a pilha estiver vazia, é porque já terminou
        if (pilhavazia())
            break;

        // Obtém o elemento atual e o custo atual
        elemento_atual = pop();
        custo_atual = pop();

        // Acrescenta o elemento no vetor de elementos já visitados
        visitados[contador++] = elemento_atual;

        // Percorre todos os caminhos possíveis a partir do formigueiro_atual
        for (i=0; i<tamanho; i++) {

            // Verifica se o formigueiro atual já foi visitado
            elemento_ja_visitado = 0;
            for (j=0; j<contador; j++)
                if (visitados[j] == i) {
                    elemento_ja_visitado = 1;
                    break;
                };
            // Pula para o próximo elemento, caso já tenha sido visitado
            if (elemento_ja_visitado)
                continue;

            // Se o formigueiro 'i' não é o próprio formigueiro atual
            if (i != elemento_atual) {
                // Verifica se existe um caminho do formigueiro atual para o 'i'
                if (f[elemento_atual][i] > 0) {
                    // Acrescenta (soma) o custo atual ao custo deste (this) caminho
                    if (custo[i] == INT_MAX) {                        
                        custo[i] = custo_atual + f[elemento_atual][i];
                    } else {
                        1;
                        /*
                         Aqui, deve se colocar a rotina para atualizar o custo
                         somente se ele for menor que o atual.
                         Não está implementado!
                        */ 
                    }
                    // Empilha o custo atual e o caminho encontrado
                    push(custo[i]);
                    push(i);
                };
            }; /*else {
                custo[elemento_atual] = 0;
            }*/
        };
    };
    printf("Custo do caminho de F%d para F%d: %d\n", inicio, fim, custo[fim]);
    return 0;
}

int main(int argc, char **argv)
{
    int **formigueiros;
    int numero_formigueiros;
    int formigueiro_aterior, distancia;
    int i, j;
    int contador;
    FILE *arquivo;

    // Abre o arquivo com os dados
    arquivo = fopen("formigueiros.txt","r");
    if (arquivo == NULL) {
        perror("Erro de arquivo.");
        exit(2);
    };

    // Lê o número de formigueiros
    fscanf(arquivo, "%d", &numero_formigueiros);
    printf("Numero de formigueiros: %d\n", numero_formigueiros);

    // Aloca e inicializa a matriz de adjacência com caminhos "nulos", com valor -1
    formigueiros = (int **) malloc(numero_formigueiros*sizeof(int *));
    for (i=0; i<numero_formigueiros; i++) {
        formigueiros[i] = (int *) malloc(numero_formigueiros*sizeof(int *));
        for (j=0; j<numero_formigueiros; j++)
            formigueiros[i][j] = -1;
    }

    // Lê os dados dos túneis
    contador = 1;
    for (i=0; i<(numero_formigueiros-1); i++) {
        fscanf(arquivo, "%d %d", &formigueiro_aterior, &distancia);
        printf("Tunel de F%d para F%d com distancia %d\n", contador, formigueiro_aterior, distancia);
        formigueiros[formigueiro_aterior][contador] = distancia;
        formigueiros[contador][formigueiro_aterior] = distancia;
        contador++;
    }
    fclose(arquivo);
    printf("\n");

    // Imprime a Matriz de adjacência
    printf("Matrix de adjacencia lida:\n");
    for (i=0; i<numero_formigueiros; i++) {
        for (j=0; j<numero_formigueiros; j++)
            printf("%d ", formigueiros[i][j]);
        printf("\n");
    };    
    printf("\n");

    // Encontra caminhos
    // Aqui, as consultas estão 'hardcoded', mas no concurso, 
    // elas devem ser lidas do arquivo
    encontra_caminho(numero_formigueiros, formigueiros, 2, 3);
    encontra_caminho(numero_formigueiros, formigueiros, 5, 2);
    encontra_caminho(numero_formigueiros, formigueiros, 1, 4);
    encontra_caminho(numero_formigueiros, formigueiros, 0, 3);
    encontra_caminho(numero_formigueiros, formigueiros, 0, 1);
    encontra_caminho(numero_formigueiros, formigueiros, 1, 0);

    // Libera a memória alocada
    for (i=0; i<numero_formigueiros; i++)
        free(formigueiros[i]);        
    free(formigueiros);

    // Aguarda o pressionamento de uma tecla para finalizar
    getchar();
    return 0;
}

The program was compiled with TDM-GCC, version: gcc version 5.1.0 (tdm64-1).

The txt file formigueiros.txt containing the data was placed in the same directory as the executable file.

After execution, the output of the program is as follows:

Numero de formigueiros: 6
Tunel de F1 para F0 com distancia 8
Tunel de F2 para F1 com distancia 7
Tunel de F3 para F1 com distancia 9
Tunel de F4 para F0 com distancia 3
Tunel de F5 para F4 com distancia 2

Matrix de adjacencia lida:
-1 8 -1 -1 3 -1
8 -1 7 9 -1 -1
-1 7 -1 -1 -1 -1
-1 9 -1 -1 -1 -1
3 -1 -1 -1 -1 2
-1 -1 -1 -1 2 -1

Custo do caminho de F2 para F3: 16
Custo do caminho de F5 para F2: 20
Custo do caminho de F1 para F4: 11
Custo do caminho de F0 para F3: 17
Custo do caminho de F0 para F1: 8
Custo do caminho de F1 para F0: 8

Only to complement:

Below is a link to a video that explains about Dijkstra’s algorithm.

Although the video is in English, you can follow the algorithm only through images:

https://www.youtube.com/watch?v=BQMjTSafmKk

5

Complementing @Gomiero’s excellent response, you can really use a simple tree to represent the connections between the anthills, having the anthill number 0 as the root node of the tree.

For each node (tingling) of the tree you will want to save four information:

  1. the code of the node;

  2. the height of the knot, that is, how many knots below the root knot it is;

  3. the cost to reach the node immediately above in the tree, the parent node;

  4. and also a reference to the parent node.

From the example given by exercise you build the following tree:

inserir a descrição da imagem aqui

Having her, for any darlings you just need to find the common parent node between the 2 nodes involved in the darlings.

I identify two main cases you need to deal with to solve the exercise:

  • To: the simplest case, in which both nodes involved in a given query are at the same height. In this, just go up the tree by the 2 nodes at the same time until you find the first parent node in common.

  • B: the most "complicated" case, when the knots are at different times. In this case you should find the deepest node (farthest from the root node), climb it up to the same height as the shallowest node (closest to the root), and then apply the same case logic To.

Example of case A:

To respond to query how much it costs to get from the knot 2 up to the knot 3 ? just look at the tree built and identify that both nodes are at the same height, then you just need to climb the tree using the reference to the parent nodes of both nodes 2 and 3 until both parent nodes are actually the same knot, always adding up the cost of the edges (tunnels between the anthills). In that case, you’ll just need to add up the costs 7 and 9, because the nodes 2 and 3 already have the same father. This will give you the final result of 16.

Example of case B:

Given to query how much it costs to get from the knot 4 to the knot 2 ? you first identify that nodes 2 and 4 are at different heights in the tree. Then you climb through the node 2 until it reaches the same height as the knot 4 (always adding up the weights of the edges), and after that only rises the two nodes at the same time until finding the father in common.

In that case the flow would be:

  • knot 2 up to the knot 1 (sum 7);

  • us 1 and 4 are at the same time;

  • knot 4 up to the knot 0 (sum 3) and at the same time knot 1 up to the knot 0 (sum 8);

  • knot 0 is identified as the common father among us 2 and 4, algorithm ends (returns 7 + 3 + 8 = 18).

Browser other questions tagged

You are not signed in. Login or sign up in order to post.