Módulo 7 · Grafos, cálculo e otimização

Grafos e caminhos mínimos

Aula 7.1 · cerca de 22 minutos

Um grafo G = (V, E) tem vértices e arestas. Dependências de pacotes, redes sociais, mapas e máquinas de estado são todos grafos. As representações mais comuns são a lista de adjacência (O(V + E) de espaço) e a matriz de adjacência (O(V²)).

Σ grau(v) = 2|E|      (lema do aperto de mãos)
Árvore com n vértices: exatamente n − 1 arestas, conexa e sem ciclos

Dijkstra

Com pesos não negativos, o vértice ainda não finalizado de menor distância já tem sua distância definitiva: qualquer desvio só somaria custo. Essa é a propriedade gulosa que torna o algoritmo correto.

import heapq

def dijkstra(grafo, origem):
    dist = {origem: 0}
    fila = [(0, origem)]
    while fila:
        d, u = heapq.heappop(fila)
        if d > dist[u]:
            continue                      # entrada obsoleta
        for v, peso in grafo[u]:
            nd = d + peso
            if nd < dist.get(v, float("inf")):
                dist[v] = nd
                heapq.heappush(fila, (nd, v))
    return dist                           # O((V + E) log V)
Dica: Com uma aresta negativa, a propriedade gulosa quebra. Use Bellman–Ford (O(V·E)), que também detecta ciclos negativos, úteis para achar arbitragem entre moedas usando −log(taxa) como peso.

Ordenação topológica

Num grafo acíclico dirigido (DAG), existe uma ordem em que toda aresta vai “para frente”. É o que npm, make e agendadores de tarefas usam para resolver dependências. Se a ordenação falha, há um ciclo.

Exercício 1

Um grafo não dirigido tem 10 vértices, todos de grau 3. Quantas arestas ele tem?

Exercício 2

Por que o Dijkstra faz continue quando d > dist[u]?