Os algoritmos de busca sugerem o seguinte conceito, que pode ser entendido como uma generalização do conceito de caminho simples. Uma arborescência (= rooted directed tree = search tree = árvore de busca) é uma sequência da forma
(v0, a1, v1, a2, v2, … , ak, vk)
onde v0, … , vk são vértices distintos dois a dois, a1, … , ak são arcos distintos dois a dois, e, para cada j, aj é um arco da forma (vi, vj) tal que
i < j.
Para cada j, dizemos que a ponta inicial de aj é o predecessor (ou pai) do vértice vj. O primeiro vértice da sequência, v0, é a raiz da arborescência. O número j é o índice ou posição do vértice vj na arborescência.
Exemplo 1: No grafo definido no exemplo 1 do capítulo O que é um grafo?, a sequência (6, d, 0, f, 4, b, 2) é uma arborescência com raiz 6. O predecessor do vértice 2 é o vértice 0. O índice de 2 é 3 e o índice de seu predecessor é 1.
Para qualquer vértice w de uma arborescência, alguma subsequência da arborescência é um caminho simples que começa na raiz v0 da arborescência e vai até w: trata-se da subsequência (v0, … , pppw, ppw, pw, w), onde pw é o predecessor de w, ppw é o predecessor de pw, etc.
Exemplo 2: No exemplo acima, o caminho que vai de 6 a 2 na arborescência é (6, d, 0, b, 2).
Como representar uma arborescência usando as convenções do SGB? Basta registrar o predecessor de cada vértice. É claro que v0 bem como os vértices fora da arborescência não têm predecessor. O predecessor de cada vértice pode ser confortavelmente acomodado em um utility field, digamos pred; assim,
vjpred
é o predecessor de vj. Podemos convencionar que v0pred vale v0 e que wpred vale NULL para todo w fora da arborescência.
É claro que essa representação envolve uma certa perda de informação: se, por exemplo, o grafo tem dois arcos da forma (v0, v1), não saberemos qual dos dois faz parte da arborescência. A prática mostra, entretanto, que é possível conviver com a ambiguidade.
Uma arborescência é maximal se não existe arco no grafo com ponta inicial na arborescência e ponta final fora dela. É fácil perceber que toda arborescência maximal com raiz r
contém todos os vértices do território de r.
É fácil deduzir daí que qualquer arborescência maximal com raiz r tem exatamente ∣T∣ − 1 arcos, onde T é o território de r.
Exemplo 3: Suponha que os vértices do grafo são cidades, que os arcos são estradas de mão única, e que r é a cidade principal. Suponha que as estradas não são pavimentadas e que desejamos pavimentar o menor número possível delas de modo que seja possível viajar de r a qualquer outra cidade (mas não necessariamente de volta) por estradas pavimentadas.
Para evitar confusão com a terminologia de outros textos, convém esclarecer que em grafos simétricos com um só componente as arborescências maximais são conhecidas como árvores geradoras (= spanning trees).
A seguinte variante do algoritmo genérico de busca constrói uma arborescência maximal com raiz r:
rpred = r
declare r ativo
enquanto existe vértice ativo faça
escolha um vértice ativo v
para cada arco a saindo de v faça
w = atip
se wpred não está definido então
wpred = v
declare w ativo
declare v inativo
A análise deste algoritmo prova que toda arborescência maximal com raiz r contém todos os vértices do território de r.
Dependendo de como o conjunto de vértices ativos é administrado, teremos uma arborescência de busca em largura ou uma arborescência de busca em profundidade (ou uma arborescência de busca que não é de nenhum dos tipos anteriores).
Problema: Dado um vértice r, encontrar, dentre as arborescências maximais com raiz r, uma que tenha comprimento mínimo.
Invente um algoritmo que resolva o problema. Escreva o algoritmo em C, usando as estruturas de dados do SGB.
O módulo MILES_SPAN do SGB apresenta várias soluções para este problema; as soluções apresentadas lá usam estruturas de dados sofisticadas para tornar o algoritmo mais rápido. O presente exercício não tem esta preocupação: basta escrever uma função simples e razoavelmente eficiente.