Professional Documents
Culture Documents
Departamento de Computação
Ouro Preto
25 de outubro de 2010
Sumário
1 Introdução 1
1.1 Considerações iniciais . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 Descrição do Trabalho . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2.1 Regras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2.2 Regras de Implementação . . . . . . . . . . . . . . . . . . . . 1
1.2.3 Formato de Entrada/Saída e Parâmetros . . . . . . . . . . . . 2
3 Conclusão 12
Lista de Programas
1 Estrutura: Item . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2 Estrutura: Heap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
3 Função: Refaz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
4 Função: Constroi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
5 Função: RetiraMin . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
6 Função: DiminuiChave . . . . . . . . . . . . . . . . . . . . . . . . . . 6
7 Função: Insere . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
8 Função: RecPrint . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
9 Função: Dijkstra . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
10 Função: recPrint . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
11 Função: vericaCicloNegativo . . . . . . . . . . . . . . . . . . . . . . 10
12 Função: FWarshal . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2
1 Introdução
Este trabalho consiste em solucionar problemas reais relacionados à grafos com o
objetivo de encontrar a melhor rota possível entre dois pontos (vértices) considerando
o menor caminhos entre esses dois vértices.
1.2.1 Regras
• Os códigos serão checados quanto a sua corretude
• ANSI C 99
• GNU C
1
1.2.3 Formato de Entrada/Saída e Parâmetros
O programa deve receber os seguintes argumentos, quando chamado pela linha
de comando:
prog <arquivoProblema> <nrExecuções> <agDepuração>
Onde <agDepuração> pode receber valor 0 ou 1. Como exemplo:
prog rome99.gr 1000 1
Indica que o programa irá ler o grafo do arquivo rome99.gr, executando o algo-
ritmo de caminhos mínimos 1000 vezes e ao nal irá imprimir/salvar informação de
depuração. A informação de depuração que deve ser gerada, quando solicitada, é a
seguinte:
arquivo spaths.txt
O arquivo spaths.txt deve conter todos os caminhos mínimos computados para
cada par de vértices (s,t), sendo s diferente de t. Cada linha contém a informação
sobre o caminho mínimo de um par (s,t), no formato:
• t: nó destino;
2
um vetor da estrutura Dados), pMatrizGrafo(matriz responsável em armazenar as
intercessões vizinhas) e pMatrizCusto (matriz para armazenar o custo/valor para
cada intercessão vizinha);
O método de comparação if(argc != 4) realiza o teste a m de vericar se o
programa recebeu o número de parâmetros adequados para dar continuidade na
execução do programa. Logo em seguida, caso os parâmetros estejam certos, o
programa tentará abrir o arquivo de entrada cujo nome foi passado como parâmetro,
caso haja algum erro, o programa retorna a seguinte mensagem para o usuário:
Problemas na abertura do arquivo.
Durante o primeiro laço de repetição do programa, com o seguinte cabeçalho
while(c!=`a`) o programa evita todo o tipo de comentário e informações que não
são importantes para a leitura do arquivo e que não devem ser incorporados pelo
programa, neste mesmo laço de repetição, o programa carrega o número de arestas
e vértices para que possa ser feita, mais adiante, a alocação dinâmica de memória.
Com a alocação dinâmica de pDados e qtdVizinhos, onde qtdVizinhos armazena
o número de vizinhos de cada vértice, é necessário colocar em todas as posições
de qtdVizinhos valores nulos. No próximo laço de repetição while (!feof(arq)) o
programa vai incorporar todas as informações importantes contidas no arquivo de
entrada(todos os arquivos de entrada obedecem ao mesmo padrão) para o vetor da
estruta Dados(como já mencionado antes, chamado de pDados). Todos os valores
são seguramente copiados para os campos certos da estrutura. Logo após o término
do laço, o programa fecha o arquivo de entrada.
Agora que já temos todo o arquivo de entrada copiado para a memória, e já
sabemos o número de vizinhos de cada vértice é o momento de gerar a matriz de
vizinho (pMatrizGrafo) e a matriz de custo (pMatrizCusto). Primeiro alocamos as
duas matrizes com <nVertices> linhas e <qtdVizinhos> colunas, onde cada inter-
cessão possui um número diferente de vizinhos(ou seja, um numero diferente de
colunas). Na primeira posição(coluna) de cada matriz, são armazenadas o número
de vizinhos que já foram adicionados em cada linha (vértice), a partir da segunda
coluna de cada linha são adicionados vizinhos da intercessão atual.
Assim que as matrizes pMatrizGrafo e pMatrizCusto já foram carregadas e preenchi-
das, não é mais necessário a utilização de pDados, neste caso basta liberar a memória
utilizada por ele. Com o vetor do tipo pDados excluído, é preciso criar dois vetores,
um para armazenar a menor distância de uma intercessão qualquer até o ponto ini-
cial (vetor chamado de distancias) e o outro para armazenar a intercessão anterior
(vetor chamado anterior).
Com estes dois novos vetores criados e alocados dinamicamente na memória, o
programa analisa o terceiro parâmetro, caso o valor seja 1 o programa cria um ar-
quivo de saída com o resultado, caso contrário o programa simplesmente executa
as instruções sem salvar em um arquivo. A partir de agora o programa irá anal-
isar o segundo parâmetro e executar as funções encontradas no arquivo digraph.h
(exemplo: dijkstra() ou fwarshall) repetitivamente, onde esta repetição esta direta-
mente associada ao valor passado no segundo parâmetro. Ambas estas funções estão
denidas e detalhadas a seguir.
3
2.2 Algoritmo de Dijkstra
Suponha que você deseja encontrar o menor caminho entre duas interseções em
uma mapa de cidades, com um ponto de partida e outro de destino. A ordem é bem
simples: para iniciar, marque a distancia de todas as intercessões como innito(isto
signica que a intercessão não foi visitada ainda). Agora, em cada iteração, selecione
a intercessão atual. Na primeira iteração a intercessão atual será o ponto inicial e a
distancia para ele mesmo será nula. Para as iterações seguintes, a intercessão atual
terá o valor da intercessão ainda e não visitada e que também seja a mais próxima
do ponto inicial.
A partir da intercessão atual, atualize a distancia para cada intercessão ainda
não visitada que possui conexão direta com ela. Isto é feito calculando o somatório
da distancia entre uma intercessão não visitada e o valor da intercessão atual, e
alterando o valor(distancia) da intercessão não visitada caso a distancia seja menor
a distancia atual. Em outras palavras, a intercessão possui seu valor alterado se o
caminho até ao ponto inicial passando pela intercessão atual é menor que os outros
caminhos já conhecidos.
Continue este processo de atualização das intercessões vizinhas com a menor
distancia, depois marcando a intercessão atual como já visitada e movendo para
a intercessão não visitada mais próxima até que você tenha chegado ao ponto de
destino. Uma vez marcado o ponto de chegada como visitado signica que você
determinou o menor caminho para ele, a partir do ponto inicial(fonte).
Neste trabalho, o algoritmo de Dijkstra se encontra no arquivo digraph.c, na
verdade não só a função de Dijkstra, mas também várias outras funções auxiliares,
além de estruturas.
2.2.1 Estruturas
A estrutura Item é responsável em armazenar o número do vértice e seu respec-
tivo custo em relação lação à fonte.
// D e f i n i c a o da E s t r u t u r a Item
typedef struct
{
5 int vertice ;
int custo ;
} Item ;
// D e f i n i c a o da E s t r u t u r a Heap
typedef struct
{
4
5 Item ∗ fila ;
int qtde ;
} Heap ;
// D e f i n i c a o da Funcao Refaz
5 int i = Esq ;
int j ;
Item aux ;
j = i ∗ 2;
aux = A[ i ] ;
if ( j < Dir )
if (A [ j ] . c u s t o > A[ j + 1 ] . c u s t o )
15 j ++;
if ( aux . c u s t o <= A [ j ] . c u s t o )
break ;
A[ i ] = A[ j ] ;
20 i = j ;
j = i ∗ 2 ;
A[ i ] = aux ;
// D e f i n i c a o da Funcao C o n s t r o i
5 int Esq ;
Esq = ∗n / 2;
R e f a z ( Esq , ∗n , A) ;
10 Esq −−;
5
}
// D e f i n i c a o da Funcao RetiraMin
5 if ( ∗n < 1)
return 0;
else
{
∗ pMinimo = A[ 1 ] ;
10 A[ 1 ] = A[ ∗n ] ;
( ∗ n ) −−;
Refaz ( 1 , ∗n , A) ;
return 1;
15 }
// D e f i n i c a o da Funcao DiminuiChave
5 Item aux ;
if ( ChaveNova > A [ i ] . c u s t o )
return 0;
A[ i ] . c u s t o = ChaveNova ;
10 {
aux = A[ i / 2 ] ;
A[ i / 2 ] = A[ i ] ;
A[ i ] = aux ;
i /= 2;
15 }
return 1;
6
Programa 6: Função: DiminuiChave
// D e f i n i c a o da Funcao I n s e r e
5 ∗ n ) ++;
(
A[ ∗ n ] = ∗x ;
A[ ∗ n ] . c u s t o = x −>c u s t o ;
}
// D e f i n i c a o da Funcao RecPrint
5 i f ( s == t || t < 0)
return ;
recPrint (p , s , p[ t ] , txt ) ;
i f ( p [ t ] >0)
f p r i n t f ( txt , " %d" , p[ t ]) ;
10 return ;
}
7
Inicialmente, o vetor dist possui todos os seus elementos com valores innitos,
a não ser o vértice de origem que possui valor zero. Já o vetor prev possui todos
os seus elementos com valores negativos, mais especicamente -1, ou seja, nenhum
deles possuem intercessões anteriores inicialmente a não ser também o vértice de
origem que possui como vértice anterior ele mesmo.
No último laço de repetição (while(tam > 0)), inicialmente, todos os vértices já
se encontram na Heap com distância innita e á medida que os vértices são explo-
rados e as distâncias encontradas são menores que a distância atual, essa distância
é atualizada para o vértice. A cada iteração o elemento de menor custo é retirado
da Heap e todos os seus vizinhos são explorados. A cada exploração podemos ter a
atualização de elementos do vetor dist e da heap.
// D e f i n i c a o da Funcao D i j k s t r a
void dijkstra ( int ∗∗ pMatGrafo , int ∗∗ pMatCusto , int S, int nVert , int
nArest , int ∗ d i s t , int ∗ p r e v , int ∗ q t d V i z i n h o s )
{
5 int i , j ;
Item ∗ heap ;
int pos ;
Item aux , u , v;
int tam = 0 ;
10
h e a p =( I t e m ∗ ) m a l l o c ( nVert ∗ ( s i z e o f ( Item ) ) ) ;
C o n s t r o i ( h e a p ,& tam ) ;
15 dist [ i ] = INT_MAX/ 3 ;
prev [ i ] = − 1;
}
20 i f ( i != S )
{
aux . c u s t o = INT_MAX/ 3 ;
aux . v e r t i c e = i ;
25 }
else
{
dist [S] = 0;
prev [ S ] = S;
30 aux . c u s t o = 0 ;
aux . v e r t i c e =S ;
40 u. vertice = pM a tG r af o [ v . v e r t i c e ] [ i ] ;
u . custo = pMatCusto [ v . v e r t i c e ] [ i ] ;
8
]) )
45 prev [ u . v e r t i c e ] = v. vertice ;
i f ( heap [ j ] . v e r t i c e==u . v e r t i c e )
50 DiminuiChave ( j , d i s t [ u . v e r t i c e ] , heap ) ;
break ;
}
55 }
f r e e ( heap ) ;
5 if (s == t || t <0)
return ;
}
10 i f ( p [ s ] [ t ] . p r e v >0)
f p r i n t f ( txt , " %d" , p [ s ] [ t ] . prev ) ;
return ;
}
9
// D e f i n i c a o da Funcao v e r i f i c a C i c l o N e g a t i v o
int v e r i f i c a C i c l o N e g a t i v o ( I t e m ∗∗ d , int i )
{
i f (d [ i ] [ i ] . dist < 0)
5 return 1 ;
return 0 ;
}
re gi ste r int i , j , k;
5 FILE ∗ arq ;
Item aux ;
i f ( MatrizGrafo [ i ][ j ] == INF_MIN )
data [ i ] [ j ] . d i s t = INF_MAX ;
15 data [ i ] [ j ] . prev = − 1;
}
else
{
data [ i ] [ j ] . d i s t = MatrizGrafo [ i ] [ j ] ;
data [ i ] [ i ] . d i s t = 0;
d a t a [ i ] [ i ] . p r e v= − 1;
25 }
10
i f ( neg >= 1)
data [ i ] [ j ] = aux ;
40 if ( i == j )
i f ( v e r i f i c a C i c l o N e g a t i v o ( data , i ))
NULL)
45 {
perror ("Nÿo f o i p o s s à v e l c r i a r o
arquivo spaths . t x t ") ;
}
data [ i ] [ i ] . d i s t ) ;
goto finaliza ;
55 }
60 }
else
{
data [ i ] [ j ] = aux ;
75 }
finaliza : 1;
11
3 Conclusão
Foi muito interessante a realização deste trabalho. Acredito que, de todos os
trabalhos práticos desenvolvidos até hoje durante o decorrer da graduação, esse foi
o que mais agregou conhecimento para nós. O fator da competição gera uma vontade
de fazer o melhor algoritmo, o mais rápido. E, com isso, conseguimos adquirir muito
conhecimento tanto na área de Grafos, quanto na área de otimização de algoritmos.
12