Aula por Lúcio Cardoso
Imagine que temos um grafo com vértices e
arestas, cada uma delas contendo um peso (um valor positivo arbitrário). Além disso, é dado um vértice
qualquer do grafo. O problema do Menor Caminho consiste em calcular, para cada vértice
, a menor distância de
para
, isto é, a menor soma possível de pesos de arestas que formem um caminho de
para
.
Como um exemplo, se definirmos a origem como o vértice
do grafo acima, teremos que:
- O menor caminho para o vértice
é
.
- O menor caminho para o vértice
é
.
- O menor caminho para o vértice
é
.
- O menor caminho para o vértice
é
.
- O menor caminho para o vértice
é
.
Nessa aula, serão abordados dois algoritmos bastante conhecidos para resolver o problema do Menor Caminho: O algoritmo de Dijkstra e o algoritmo de Floyd-Warshall.
Algoritmo de Dijkstra
O algoritmo de Dijkstra é usado para resolver o problema citado acima. Ele consiste das seguintes etapas:
- Inicialmente, marque todos os vértices do grafo como não visitados. Além disso, defina a distância atual para
(origem) como
e para todos os outros vértices como
.
- Encontre, dentre os vértices não visitados, aquele com menor distância atual. Chame-o de
e marque-o como visitado. Assim, a distância atual de
é, de fato, a sua menor distância para
e
não será visitado novamente.
- Para todo vizinho de
de
, cheque se
, onde
é a distância atual de
,
é o menor caminho de
e
é o peso da aresta que liga
e
. Se sim, atualize a distância atual de
para
.
- Repita as etapas
e
até que todos os vértices do grafo estejam marcados.
Por que o Algoritmo de Dijkstra funciona?
Suponha que em um momento qualquer do algoritmo, o vértice é o vértice não marcado mais próximo de
, ou seja, aquele cujo menor caminho para
é mínimo. Perceba que o menor caminho de
para
é composto pelo menor caminho de
para algum vértice
seguido de uma aresta de
para
. Como as arestas tem pesos positivos, o menor caminho de
é certamente menor que o menor caminho de
. Logo, como
é definido como o vértice não marcado de menor caminho mínimo, teremos que o vértice
certamente já foi marcado. Porém, assim que
for marcado, a etapa
do algoritmo será realizada em seus vizinhos, e em particular, a distância atual de
será atualizada para
, que é exatamente o menor caminho de
!
Queremos agora provar que o vértice escolhido na etapa é exatamente o vértice
. Suponha que o vértice não marcado de menor distância atual é um vértice
. Então, teremos que
.
Como mostrado acima,
.
Mas
(por definição)
uma contradição, já que foi definido como o vértice não marcado de menor caminho mínimo. Logo,
será o vértice escolhido na etapa
do algoritmo, e além disso, sua distância atual será igual à sua menor distância para
, o que conclui a prova.
Para implementar o algoritmo de Dijkstra, representaremos o nosso grafo como uma Lista de Adjacência. Além disso, vamos usar uma Fila de Prioridade que irá guardar pares da forma , ou seja, a fila será ordenada de maneira crescente pela distância atual de cada vértice. Isso será de grande utilidade para realizarmos a etapa
de maneira eficiente.
Confira o código abaixo:
Complexidade do algoritmo
Note que para cada vértice , ao atualizarmos as distâncias dos seus vizinhos na etapa
, o número máximo de vértices que serão adicionados na fila é
, onde
é o grau (quantidade de vizinhos) do vértice
. Portanto, a quantidade total máxima de vértices que serão adicionados na fila é igual a
, já que, neste somatório, cada aresta do grafo será contada duas vezes.
Finalmente, como as operações da Fila de Prioridade possuem complexidade , a complexidade do algoritmo de Dijkstra será
.
Algoritmo de Floyd-Warshall
O algoritmo de Floyd-Warshall é utilizado para calcular a menor distância entre todos os pares de vértices do grafo, diferentemente do algoritmo de Dijkstra. A ideia do algoritmo consiste em perceber que todo menor caminho com mais de 2 vértices pode ser decomposto em dois menores caminhos:
e
, onde
é um vértice intermediário no caminho
.
Vamos definir como a menor distância do vértice
para o vértice
considerando que o caminho
possui apenas vértices intermediários com índice menor ou igual a
. Assim, para cada par
de vértices, teremos que
=
onde é o peso da aresta
. Se
, então
, e se não existe uma aresta ligando
e
, então definiremos
como
. Perceba que
será exatamente igual ao menor caminho de
para
, já que poderemos usar qualquer vértice como vértice intermediário.
A implementação do algoritmo de Floyd-Warshall é bastante simples. Confira o código abaixo:
Perceba que durante o código, não é necessário guardar - podemos guardar apenas
representando a menor distância atual de
para
, já que quando utilizamos o vértice
como intermediário,
é exatamente igual a
.
Complexidade do algoritmo
A complexidade do algoritmo de Floyd-Warshall é extremamente simples de se calcular. Para cada vértice intermediário de 1 a
, atualizaremos a distância de cada par de vértices
. Logo, a complexidade do algoritmo é
.
Observação
Os algoritmos apresentados acima funcionam em grafos com arestas de pesos não-negativos e não funcionam caso alguma delas tenha peso negativo.
Problemas para Praticar
Tente resolver os problemas a seguir de variados níveis de dificuldade para fixar os conteúdos vistos e exercitar sua criatividade.
- Caminho das Pontes
- Reunião
- Lanche na Empresa
- Quase Menor Caminho
- Arbitrage
- Países em Guerra
- Fine Dining (em inglês)
- Relógios
- King Gruff (em inglês)