Caminhos simples de custo máximo
Este capítulo é uma introdução geral ao difícil e traiçoeiro
problema dos caminhos simples
de custo máximo em grafos com custos nos arcos.
Vamos supor que os custos de todos os arcos são
positivos,
embora a maior parte dos conceitos e observações deste capítulo
façam sentido com custos arbitrários.
O problema
Num grafo com custos nos arcos,
o custo de um caminho
é a soma dos custos dos arcos do caminho.
Convém lembrar que,
por definição,
caminhos não têm arcos repetidos.
Um caminho D é mais caro
que um caminho C se tiver
custo maior que o de C.
Dizemos que um caminho C
tem custo máximo
se nenhum caminho
com a mesma origem e o mesmo término que C
for mais caro que C.
Problema do caminho simples de custo máximo:
Dados vértices s e t de
um grafo com custos positivos nos arcos,
encontrar um caminho simples de custo máximo
que tenha origem s e término t.
A qualificação simples
não é redundante nesse problema,
diferentemente do que acontece com caminhos de custo mínimo.
O problema tem solução se e somente se
t está ao alcance de s.
A propósito,
se s ≡ t
então a única solução do problema é
o caminho trivial de comprimento 0.
Um caso particular célebre do problema
é o do caminho hamiltoniano:
encontrar um caminho simples de s a t
que passe por todos os vértices do grafo.
(Quanto ao problema do ciclo hamiltoniano,
ele não é um caso particular do nosso problema
pois sua solução tem um vértice repetido.)
Exemplo A.
Considere o grafo (não-dirigido)
definido pelo conjunto de arcos abaixo.
Suponha que todos os arcos têm custo 1.
Um caminho simples de custo máximo
do vértice 0 ao vértice 2
tem custo 4.
0-1 1-0 1-2 2-1 0-3 3-0 1-3 3-1 1-4 4-1 2-4 4-2
Exemplo B.
Considere o grafo (não-dirigido)
definido pelo conjunto de arcos abaixo.
Suponha que todos os arcos têm custo 1.
Um caminho simples de custo máximo
do vértice 0 ao vértice 3
tem custo 2.
0-1 1-0 1-2 2-1 1-3 3-1
Exemplo C.
Considere o grafo definido pelo conjunto de arcos abaixo.
Suponha que todos os arcos têm custo 1.
Um caminho simples de custo máximo
do vértice 0 ao vértice 4
tem custo 2.
(Note que há um caminho de 0 a 4
que tem custo 5.)
0-1 1-2 2-3 3-1 1-4
Exercícios 1
-
Seja s um vértice de um grafo com custos arbitrários
(positivos e negativos)
nos arcos.
Qual o custo de um caminho simples de custo máximo de s a s?
-
Encontre um caminho de custo máximo de 0 a 7
no grafo com custos nos arcos definido a seguir.
0-7 0-1 1-2 2-3 3-4 4-5 5-6 6-7
60 10 10 10 10 10 10 10
-
Ciclo hamiltoniano.
Considere o seguinte problema:
dado um grafo,
encontrar um ciclo simples que passe por todos os vértices do grafo.
Mostre como podemos decidir se
esse problema tem solução
usando um algoritmo para o problema do caminho simples de custo máximo.
-
Circuito hamiltoniano.
Considere o seguinte problema:
dado um grafo não-dirigido,
encontrar um circuito simples que passe por todos os vértices do grafo.
Mostre como podemos decidir se
esse problema tem solução
usando um algoritmo para o problema do caminho simples de custo máximo.
-
Caminho hamiltoniano.
Considere o seguinte problema:
encontrar um caminho simples que passe por todos os vértices
de um grafo não-dirigido.
Mostre como podemos decidir se
esse problema tem solução
usando um algoritmo para o problema do caminho simples de custo máximo.
-
Grafo não-dirigido.
Considere o seguinte problema:
dados vértices s e t de
um grafo não-dirigido com custos positivos
nas arestas,
encontrar um caminho simples de s a t
que tenha o maior custo possível.
Mostre como esse problema pode ser resolvido
usando um algoritmo para o problema do caminho simples de custo máximo.
-
Para quaisquer dois vértices s e t
de cada um dos grafos abaixo,
encontre um caminho simples de comprimento máximo
de s a t.
Encontre um ciclo simples de comprimento máximo
em cada um dos grafos.
-
Considere o seguinte problema:
encontrar um ciclo simples de custo máximo
num grafo com custos positivos
nos arcos.
Mostre como esse problema pode ser resolvido
usando um algoritmo para o problema do caminho simples de custo máximo.
-
Considere o seguinte problema:
encontrar um circuito simples de custo máximo
num grafo não-dirigido com custos positivos
nas arestas.
Mostre como esse problema pode ser resolvido
usando um algoritmo para o problema do caminho simples de custo máximo.
-
Custos negativos.
Mostre que o problema do caminho simples de custo mínimo
num grafo com custos negativos nos arcos
é equivalente ao problema do caminho simples de custo máximo
com custos positivos.
-
Dado um grafo com custos positivos nos arcos,
quero encontrar um caminho simples de custo máximo que
vá de um vértice s a um vértice t.
Analise e discuta a seguinte ideia:
para resolver esse problema,
multiplique o custo de cada arco por −1
e depois use o algoritmo de Bellman-Ford.
Um algoritmo para dags
Quando restrito a dags,
o problema do caminho simples de custo máximo é fácil.
Em um dag, todos os caminhos são simples
e portanto o problema pode ser reformulado assim:
Problema do caminho de custo máximo em dags:
Dados vértices s e t de
um dag com custos positivos nos arcos,
encontrar um caminho de custo máximo
que tenha origem s e término t.
A função DAGcpt()
do capítulo Caminhos baratos em dags
pode ser usada para resolver o problema:
basta inverter o sinal de todos os custos!
Outra possibilidade é deixar os custos inalterados
e trocar
INT_MAX
e dist[v]+a->c < dist[a->w]
por
INT_MIN
e dist[v]+a->c > dist[a->w]
,
respectivamente,
no código de DAGcpt().
Essas duas variantes de DAGcpt()
são muito eficientes:
consomem tempo proporcional a V + A
no pior caso,
sendo V o número de vértices
e A o número de arcos do dag.
Exercícios 2
-
Caminho máximo.
Considere o dag com custos nos arcos definido abaixo.
A sequência 5 1 3 6 4 7 0 2
é uma permutação topológica dos vértices.
Verifique que a árvore destacada na figura
é uma árvore de caminhos máximos
(= longest-paths tree)
com raiz 5.
(Ignore a cor vermelha do vértice 0
e o fundo cinza do vértice 2.)
5-4 4-7 5-7 5-1 4-0 0-2 3-7 1-3 7-2 6-2 3-6 6-0 6-4
35 37 28 32 38 26 39 29 34 40 52 58 93
-
★
Caminho de comprimento máximo em dag.
Considere o seguinte problema:
Dado um dag sem custos nos arcos,
encontrar um caminho simples C
tal que nenhum outro caminho —
quaisquer que sejam sua origem e seu término —
tenha comprimento maior que o de C.
(É claro que algum caminho desse tipo começa numa fonte
e termina num sorvedouro.)
Escreva uma função que resolva o problema.
O consumo de tempo de sua função deve ser proporcional,
no pior caso,
ao número de vértices do dag.
-
★
PERT/CPM.
Considere o seguinte problema:
Dado um dag com custos positivos nos arcos,
encontrar um caminho simples C
tal que nenhum outro caminho —
quaisquer que sejam sua origem e seu término —
tenha custo maior que o de C.
(É claro que algum caminho desse tipo começa numa fonte
e termina num sorvedouro.)
Esse problema surge naturalmente
na administração de grandes projetos de engenharia
que usam o Program Evaluation and Review Technique
e o Critical Path Method
para fazer o escalonamento de tarefas.
Os arcos representam tarefas
do projeto e os vértices representam estágios do projeto.
Uma tarefa v-w só pode começar depois que todos os arcos
que desembocam em v estiverem concluídas.
O custo de cada arco representa a duração da correspondente tarefa.
O custo de um caminho de custo máximo é a duração total do projeto.
Qualquer atraso em uma das tarefas no caminho de custo máximo
aumenta o tempo de execução do projeto todo.
Por isso, o caminho de custo máximo é um caminho crítico.
-
Dual do teorema de Dilworth.
Seja G um dag sem custos nos arcos.
Mostre que um caminho de comprimento máximo
tem comprimento igual
ao tamanho de uma cobertura de tamanho mínimo.
Uma cobertura
é um conjunto K0
K1
…
Kj−1
de anticadeias tal que todo vértice de G
pertence a pelo menos uma Ki.
O tamanho da cobertura é o número j.
Uma anticadeia (= antichain)
é um conjunto K de vértices de G
tal que não existe caminho em G
que comece e termine em K.
Algoritmo de força bruta
O problema do caminho simples de custo máximo é difícil.
Parte da dificuldade está na exigência de que os caminhos
sejam simples.
Mas a maior dificuldade decorre da exigência de custo máximo.
Nenhum algoritmo razoavelmente rápido foi encontrado ainda.
O problema não fica mais fácil
se todos os arcos tiverem custo 1.
Também não fica mais fácil se restrito a grafos não-dirigidos.
Examinaremos a seguir um algoritmo de força bruta
que calcula o custo de um caminho simples de custo máximo
de s a t
(embora não exiba o caminho propriamente dito).
O algoritmo é extremamente lento.
pois examina todos os caminhos simples
de s a t.
Embora a ideia seja simplória,
o código não é inteiramente óbvio.
static bool mark[1000];
/* Recebe um grafo G com custos positivos nos arcos
e vértices s e t.
Devolve o custo de um caminho simples de custo máximo
de s a t.
(Em particular, devolve 0 se s == t.)
Se não existe caminho algum de s a t,
devolve -1 (que representa infinito negativo).
Cuidado! Esta função só é razoável para grafos muito pequenos.
(O código foi inspirado no programa 17.12
de Sedgewick.) */
int GRAPHmaxCostPath( Graph G, vertex s, vertex t)
{
for (vertex v = 0; v < G->V; ++v)
mark[v] = false;
return dfsRmaxCostPath( G, s, t);
}
/* Esta função recebe um grafo G
com custos positivos nos arcos, um conjunto de vértices marcados,
e vértices não marcados v e t
(possivelmente iguais). A função examina o conjunto, digamos C,
de todos os caminhos simples de v a t
que não têm vértices marcados.
Se C é vazio, devolve -1.
Senão, devolve o custo do caminho mais caro de C.
(Em particular, devolve 0 se v == t.)
A função não altera o conjunto de vértice marcados. */
static int dfsRmaxCostPath( Graph G, vertex v, vertex t)
{
if (v == t)
return 0;
mark[v] = true;
int m = -1;
for (link a = G->adj[v]; a != NULL; a = a->next) {
if (!mark[a->w]) {
int mw = dfsRmaxCostPath( G, a->w, t);
if (mw != -1 && a->c + mw > m)
m = a->c + mw;
}
}
mark[v] = false; // atenção! backtrack
return m;
}
Esse algoritmo usa uma variante da busca em profundidade
conhecida como backtracking.
A única diferença com relação ao
código da busca em profundidade usual
(veja o capítulo Acessibilidade)
está na penúltima linha de dfsRmaxCostPath(),
que desmarca o vértice v
restaurando o valor original de mark[v].
Isso garante que todos os caminhos simples sejam examinados.
Desempenho.
O algoritmo examina todos os caminhos simples de s a t.
Isso equivale, no pior caso, a examinar todas as permutações
dos vértices do grafo.
Um grafo com V vértices tem
V !
tais permutações,
e esse número é bem maior que 2V.
Assim,
o consumo de tempo de GRAPHmaxCostPath()
cresce mais que exponencialmente com o tamanho do grafo.
Portanto, o algoritmo só pode ser aplicado a grafos
muito pequenos.
Exercícios 3
-
Prove cuidadosamente que a função dfsRmaxCostPath() está correta,
ou seja,
que devolve o custo do caminho mais caro dentre os
caminhos simples de v a t que não têm vértices marcados
(a menos que um tal caminho não existe).
-
Supondo que a função dfsRmaxCostPath() está correta,
prove cuidadosamente que a função GRAPHmaxCostPath()
está correta.
-
[Sedgewick 17.107]
Considere o grafo não-dirigido
definido pelo conjunto de arestas
1-2 5-2 4-2 2-6 0-8 3-0 1-3 3-6 1-0 1-4 4-0 4-6 6-5 6-9 9-0 3-1 4-3 9-2 4-9 7-9 5-0 9-7 7-3 4-5 0-5 7-8 .
Suponha que todas as arestas têm custo 1.
Encontre um ciclo hamiltoniano
(ou mostre que um tal ciclo não existe).
-
É possível modificar os códigos de
GRAPHmaxCostPath() e dfsRmaxCostPath()
de modo a imprimir um caminho de custo máximo?
-
Reescreva dfsRmaxCostPath() de modo que a parte central do código
fique como indicado a seguir.
Reescreva o código de GRAPHmaxCostPath()
de acordo com a nova versão de dfsRmaxCostPath().
mark[a->w] = true;
int mw = dfsRmaxCostPath( G, a->w, t);
mark[a->w] = false;
-
A função GRAPHmaxCostPath() produz resultados corretos
se alguns (ou todos) arcos tiverem custo negativo?
-
Caminhos de comprimento especificado.
Escreva uma variante de GRAPHmaxCostPath()
que tenha uma parâmetro adicional inteiro positivo d
e decida se existe um caminho simples de s a t
de comprimento d.
-
Critique a seguinte proposta de solução do
problema do caminho simples de custo máximo:
Inverta todos os custos
(ou seja, troque c por 1/c)
e calcule caminho de custo mínimo
de s a t.
-
Desafio.
Escreva um algoritmo que
consuma tempo limitado por um polinômio no tamanho do grafo
para resolver o
problema do caminho simples de custo máximo.