• Nenhum resultado encontrado

Algoritmo de programação dinâmica

No documento Minicurso de Análise de Algoritmos (páginas 44-49)

Para tirar bom proveito da recorrência (8.2), é preciso recorrer à técnica da programação dinâmica (veja a Seção 5.5), que consiste em guardar as soluções das subinstâncias

ANÁLISE DE ALGORITMOS 45

numa tabela à medida que elas forem sendo resolvidas. Com isso, cada subinstância será resolvida uma só vez.

O seguinte algoritmo recebe n intervalos que satisfazem (8.1) e devolve o valor de um svvm de {1, . . . , n}:

INTERVALOSCOMPATÍVEIS(a1, . . . , an, b1, . . . , bn, v1, . . . , vn)

1 X[0] ← 0

2 para m ← 1 até n faça

3 j ← m − 1 4 enquanto j ≥ 1 e bj ≥ amfaça 5 j ← j − 1 6 se X[m − 1] > X[j] + vm 7 então X[m] ← X[m − 1] 8 senão X[m] ← X[j] + vm 9 devolva X[n]

(Não é difícil extrair da tabela X um svvm de {1, . . . , n}.)

O algoritmo está correto. Na linha 2, imediatamente antes da comparação de m com n,

X[m−1]é o valor de um svvm de {1, . . . , m−1}, X[m−2]é o valor de um svvm de {1, . . . , m−2}, . . . , X[1]é o valor de um svvm de {1}.

Este invariante certamente vale no início da primeira iteração, quando m = 1. Supo- nha agora que ele vale no início de uma iteração qualquer. Para mostrar que continua valendo no início da iteração seguinte, observe que, no começo da linha 6, {1, . . . , j} é o conjunto dos intervalos compatíveis com m. Assim, o valor atribuído a X[m] nas linhas 7 e 8 está de acordo com a recorrência (8.2) e portanto X[m] é o valor de um svvm de {1, . . . , m}.

Na última passagem pela linha 2 temos m = n + 1 e portanto X[n] é o valor de um svvm de {1, . . . , n}. Assim, o algoritmo cumpre o que prometeu.

Consumo de tempo. Uma execução de qualquer linha do algoritmo INTERVA-

LOSCOMPATÍVEISconsome uma quantidade de tempo que não depende de n. Logo, o

consumo de tempo total do algoritmo pode ser medido contando o número de execu- ções das diversas linhas.

Para cada valor fixo de m, a linha 5 é executada no máximo m − 1 vezes. Como m varia de 1 a n ePn

m=1(m − 1) = 12(n

2− n), a linha 5 é executada menos que n2vezes.

Todas as demais linhas são executadas no máximo n − 1 vezes. Logo, o consumo de tempo total do algoritmo está em

O(n2)

no pior caso. Uma análise um pouco mais cuidadosa mostra que o consumo está em Θ(n2)no pior caso.

Não levamos em conta ainda o pré-processamento que rearranja os intervalos em ordem crescente de término. Como esse pré-processamento pode ser feito em tempo O(n lg n)(veja o Capítulo4) e n lg n está em O(n2), o consumo de tempo total é Θ(n2). (Mas veja o Exercício8.3.)

Exercícios

8.3 Mostre que o algoritmo INTERVALOSCOMPATÍVEISpode ser implementado de modo a con- sumir apenas O(n) unidades de tempo. Sugestão: construa uma tabela auxiliar que pro- duza, em tempo constante, efeito equivalente ao das linhas 4-5.

8.4 Escreva um algoritmo de pós-processamento que extraia da tabela X[1 . . n] produzida pelo algoritmo INTERVALOSCOMPATÍVEISum svvm de {1, . . . , n}. O seu algoritmo deve con- sumir O(n) unidades de tempo.

Notas bibliográficas

O problema dos intervalos valorados é discutido no livro de Kleinberg e Tardos [10]. O verbeteInterval schedulingna Wikipedia trata do escalonamento de intervalos.

Capítulo 9

As linhas de um parágrafo

Um parágrafo de texto é uma sequência de palavras, sendo cada palavra uma sequên- cia de caracteres. Queremos imprimir um parágrafo em uma ou mais linhas consecuti- vas de uma folha de papel de tal modo que cada linha tenha no máximo L caracteres. As palavras do parágrafo não devem ser quebradas entre linhas e cada duas palavras consecutivas numa linha devem ser separadas por um espaço.

Para que a margem direita fique razoavelmente uniforme, queremos distribuir as palavras pelas linhas de modo a minimizar a soma dos cubos dos espaços em branco que sobram no fim de todas as linhas exceto a última.

9.1

O problema

Para simplificar a discussão do problema, convém numerar as palavras do parágrafo em ordem inversa e confundir as palavras com os seus comprimentos. Diremos, pois, que um parágrafo é uma sequência cn, cn−1, . . . , c2, c1 de números naturais. Diremos

também que cada cié uma palavra: cné a primeira palavra do parágrafo e c1é a última.

Um trecho de um parágrafo cn, cn−1, . . . , c1 é qualquer sequência da forma

cm, cm−1, . . . , ckcom n ≥ m ≥ k ≥ 1. Este trecho será denotado por (m, k). O compri-

mentodo trecho (m, k) é o número

c(m, k) := cm+ 1 + cm−1+ 1 + · · · + 1 + ck.

Os próximos conceitos envolvem um número natural L que chamaremos capacidade

de uma linha. Um trecho (m, k) é curto se c(m, k) ≤ L. O defeito de um trecho curto (m, k)é o número

(L − c(m, k))3.

Uma L-decomposição do parágrafo cn, cn−1, . . . , c1é uma subdivisão do parágrafo em

trechos curtos. Mais precisamente, uma L-decomposição é uma sequência (n, kq), (kq− 1, kq−1), (kq−1− 1, kq−2), . . . , (k2− 1, k1) (k1− 1, 1)

de trechos curtos em que n ≥ kq > kq−1 > · · · > k2 > k1 > 1. O defeito de uma

decomposição é a soma dos defeitos de todos os trechos da decomposição exceto o último. Uma decomposição é ótima se tem defeito mínimo.

Aaaa bb cccc d eee ff gggg iii jj kkk. Llll mmm nn ooooo--- ppppp qq r sss ttt uu. Aaaa bb cccc d eee ff gggg---- iii jj kkk. Llll mmm nn ooooo- ppppp qq r sss ttt uu.

Figura 9.1: Duas decomposições do mesmo parágrafo. As linhas têm capaci- dade L = 30 e os caracteres “-” representam os espaços em branco que contri- buem para o defeito. O defeito da primeira decomposição é 03+ 53 = 125e o da

segunda é 43+ 13= 65.

ccc bbb ccc

aaa bbb-- ccc

Figura 9.2: À direita temos uma decomposição ótima de um parágrafo c3, c2, c1. A

capacidade das linhas é L = 9. Os caracteres “-” representam os espaços em branco que contribuem para o defeito. No centro, temos uma decomposição ótima do pará- grafo c2, c1. À esquerda, uma decomposição ótima do parágrafo c1.

Problema da Decomposição de um Parágrafo: Dado um parágrafo cn, cn−1, . . . ,

c1e um número natural L, encontrar uma L-decomposição ótima do parágrafo.

É claro que as instâncias do problema em que ci > Lpara algum i não têm solução.

Exercícios

9.1 Tente explicar por que a definição de defeito usa a terceira potência e não a primeira ou a segunda.

9.2 Considere a seguinte heurística “gulosa”: preencha a primeira linha até onde for possível, depois preencha o máximo possível da segunda linha, e assim por diante. Mostre que esta heurística não resolve o problema.

9.3 Suponha que ci = 1para i = n, . . . , 1. Mostre que o defeito de uma decomposição ótima é

no máximo b2n/Lc.

9.2

A estrutura recursiva do problema

O problema da decomposição de parágrafos tem caráter recursivo, como passamos a mostrar. Suponha que (n, k) é o primeiro trecho de uma decomposição ótima do

ANÁLISE DE ALGORITMOS 49

parágrafo cn, cn−1, . . . , c1. Se k ≥ 2 então

a correspondente decomposição de ck−1, ck−2, . . . , c1é ótima.

A prova desta propriedade é simples. Seja x o defeito da decomposição de ck−1, . . . ,

c1induzida pela decomposição ótima de cn, . . . , c1. Suponha agora, por um momento,

que o parágrafo ck−1, . . . , c1tem uma decomposição com defeito menor que x. Então a

combinação desta decomposição com o trecho (n, k) é uma decomposição de cn, . . . , c1

que tem defeito menor que o mínimo, o que é impossível.

A propriedade recursiva pode ser representada por uma relação de recorrência. Seja X(n) o defeito de uma decomposição ótima do parágrafo cn, cn−1, . . . , c1. Se o

trecho (n, 1) é curto então X(n) = 0. Caso contrário, X(n) = min

c(n,k)≤L (L − c(n, k))

3+ X(k−1) , (9.1)

sendo o mínimo tomado sobre todos os trechos curtos da forma (n, k).

A recorrência poderia ser transformada facilmente num algoritmo recursivo. Mas o cálculo do min em (9.1) levaria o algoritmo a resolver as mesmas subinstâncias várias vezes, o que é muito ineficiente.

No documento Minicurso de Análise de Algoritmos (páginas 44-49)

Documentos relacionados