O problema da subsequência crescente máxima e suas variações está por trás de várias questões computacionais básicas da ciência e da engenharia. A resolução do problema é uma boa introdução ao método de programação dinâmica.
Esta página é inspirada na seção 15.4 de livro CLRS, que trata do problema conhecido como LCS (= longest common subsequence) problem.
Seja que A[1 .. n] é uma sequência de números. (Nesta página, uma sequência é o mesmo que um vetor.) Uma subsequência de A[1 .. n] é o que sobra depois que um conjunto arbitrário de elementos da sequência é apagado. Para melhor entender as sutilezas do conceito, convém dar uma definição mais formal.
Uma sequência S[1 .. k] é subsequência de uma sequência A[1 .. n] se existe uma injeção de S em A. Uma injeção de S em A é um sequência (i1, i2, … , ik) de índices tal que 1 ≤ i1 < i2 < ⋯ < ik ≤ n e
S[1] = A[i1], S[2] = A[i2], … , S[k] = A[ik] .
Dizemos que o índice 1 de S casa com o índice i1 de A, que o índice 2 de S casa com o índice i2 de A, etc. De maneira mais geral, dizemos que um índice j de S casa com um índice m de A se existe uma injeção (i1, i2, … , ik) tal que ij = m.
Uma subsequência S[1 .. k]
de A[1 .. n]
é crescente
se S[1] ≤ S[2] ≤
⋯ ≤
S[k].
Adotaremos a abreviatura SSC
para dizer subsequência
crescente
.
Uma SSC de A[1 .. n]
é máxima
se não existe uma SSC mais longa.
Problema da SSC máxima: Encontrar uma SSC máxima de uma sequência A[1 .. n].
Para simplificar, vamos tratar apenas de encontrar o comprimento de uma SSC máxima. Mas qualquer algoritmo que encontre o comprimento poderá ser adaptado para encontrar a correspondente SSC. Além disso, vamos supor que a sequência A[1 .. n] não é vazia, ou seja, que n ≥ 1. Com isso, toda instância do problema tem uma solução de comprimento não nulo.
Exemplo A: Seja A a sequência (9, 5, 6, 5, 9, 6, 4, 7) e S a sequência (5, 6, 6, 7). Observe que (2, 3, 6, 8) é uma injeção de S em A e portanto S é subsequência de A. O índice 2 de S casa com o índice 3 de A e o índice 3 de S casa com índice 6 de A. Embora S[1] seja igual a A[4], o índice 1 de S não casa com o índice 4 de A.
9 | 5 | 6 | 5 | 9 | 6 | 4 | 7 |
5 | 6 | 6 | 7 |
A sequência S é uma SSC de A. A sequência (5, 6, 9) também é uma SSC de A. Esta última não é máxima pois S é mais longa. (A sequência (5, 6, 9) é apenas uma SSC maximal de A, pois não pode ser prolongada.)
9 | 5 | 6 | 5 | 9 | 6 | 4 | 7 |
5 | 6 | 9 |
Guloso (A, n) |
1 . k := Último (A, n) |
2 . S[k] := A[n] |
3 . para i := n − 1 decrescendo até 1 |
4 .ooo se A[i] ≤ S[k] |
5 .ooooooo S[k−1] := A[i] |
6 .ooooooo k := k − 1 |
7 . devolva n − k + 1 |
O caminho para a solução do problema da SSC máxima passa por um problema auxiliar. Para enunciar esse problema, precisamos da seguinte definição: uma subsequência direita de A[1 .. n] é qualquer subsequência D[1 .. k] de A[1 .. n] tal que o índice k de D casa com o índice n de A. É fácil constatar que uma subsequência D[1 .. k] de A[1 .. n] é direita se e somente se D[k] = A[n].
Estamos interessados em subsequências direitas que são crescentes.
Adotaremos a abreviatura SSDC
para a expressão subsequência direita crescente
.
Agora podemos enunciar o problema auxiliar:
Problema da SSDC máxima: Encontrar uma SSDC máxima de uma sequência A[1 .. n].
Exemplo B. A figura mostra uma sequência A[1 .. 10], seguida de uma SSC máxima S[1 .. 6] e de uma SSDC máxima D[1 .. 5]. Note que D é mais curta que S. (Em geral, o comprimento de uma SSDC máxima é menor que ou igual ao comprimento de uma SSC máxima.)
A | 40 | 11 | 90 | 22 | 33 | 50 | 60 | 44 | 70 | 55 |
S | 11 | 22 | 33 | 50 | 60 | 70 | ||||
D | 11 | 22 | 33 | 44 | 55 |
A relação entre o problema auxiliar e o problema original é simples: uma SSC máxima de A[1 .. n] é a mais longa das SSDCs máximas de A[1 .. m] para m = 1, 2, … , n. Se c[m] é o comprimento de uma SSDC máxima de A[1 .. m] então o seguinte algoritmo devolve o comprimento de uma SSC de A[1 .. m]:
. x := 0 |
. para m := 1 até n |
.ooo se c[m] > x |
.oooooo x := c[m] |
. devolva x |
Resta apenas calcular c[m] para m = 1, 2, … , n. Para isso, é preciso resolver o problema auxiliar. É o que faremos a seguir.
Para resolver o problema da SSDC máxima, vamos recorrer à técnica da programação dinâmica. A instância A[1 .. n] do problema é resolvida a partir das subinstâncias A[1 .. m]. Os comprimentos das soluções das subinstâncias são guardados numa tabela c[1 .. n] e cada c[m] é calculado a partir de c[1 .. m−1].
SSDC-Max-Prog-Din (A, n) |
1 . para m := 1 até n |
2 .ooo c[m] := 1 |
3 .ooo para i := m−1 decrescendo até 1 |
4 .oooooo se A[i] ≤ A[m] e c[i] + 1 > c[m] |
5 .ooooooooooo c[m] := c[i] + 1 |
6 . devolva c[1 .. n] |
No fim de cada execução do bloco de linhas 2-5, c[m] é o comprimento de uma SSDC máxima de A[1 .. m] , como mostraremos a seguir.
Exemplo C. A figura mostra uma sequência A[1 .. 10] e a correspondente tabela c[1 .. 10]. O algoritmo preenche a tabela da esquerda para a direita.
A | 40 | 11 | 90 | 22 | 33 | 50 | 60 | 44 | 70 | 55 |
c | 1 | 1 | 2 | 2 | 3 | 4 | 5 | 4 | 6 | 5 |
O algoritmo está correto. Cada execução do bloco de linhas 2-5 do algoritmo calcula c[m] a partir de c[1 .. m−1] usando a recorrência
c[m] = max c[i] + 1 , | (A) |
onde o máximo é tomado sobre todos os índices i
em 1 .. m−1 para os quais
A[i] ≤ A[m].
O valor inicial
da recorrência
é c[m] = 1,
e esse valor se aplica aos casos em que não existe i
em 1 .. m−1 tal que
A[i] ≤ A[m].
Para mostrar que a recorrência (A) tem o efeito desejado,
é preciso provar que o problema da SSDC máxima
é dotado da seguinte estrutura recursiva:
se D[1 .. k] é uma SSDC máxima de A[1 .. m] e k ≥ 2 então existe i em 1 .. m−1 tal que D[1 .. k−1] é uma SSDC máxima de A[1 .. i].
Desempenho.
É fácil ver que o consumo de tempo do algoritmo SSDC-Max-Prog-Din
é proporcional ao número de execuções da comparação
A[i] ≤ A[m]
na linha 4.
Esse número é
∑1 ≤ m ≤ n (m − 1) = n(n−1)/2.
Portanto, o algoritmo consome
unidades de tempo, tanto no pior quanto no melhor caso. (Outra maneira de ver isso: a tabela c tem n posições e o cálculo do valor final de cada posição consome no máximo n unidades de tempo.)
o algoritmo faz isso, depois faz aquilo …, etc.)?
Agora que resolvemos o problema auxiliar podemos voltar ao problema da SSC máxima. Para obter o comprimento de uma SSC máxima de A[1 .. n], basta encontrar o máximo da tabela c[1 .. n]:
SSC-Max-PD (A, n) |
1 . c := SSDC-Max-Prog-Din (A, n) |
2 . x := 0 |
3 . para i := 1 até n |
4 .ooo se c[i] > x |
5 .oooooo x := c[i] |
6 . devolva x |
A linha 1 consome Θ(n²) unidades de tempo e as demais linhas consomem Θ(n) unidades de tempo. Portanto, o algoritmo é quadrático.
SSC-Maxx (A, n) |
1 . c[0] := 0 |
2 . para m := 1 até n |
3 .ooo i := m |
4 .ooo repita i := i−1 |
5 .oooooo até que i = 0 ou A[i] ≤ A[m] |
6 .ooo c[m] := max (c[m−1], c[i]+1) |
7 . devolva c[n] |
O problema da SSC máxima goza de uma propriedade minimax interessante. (Trata-se de uma manifestação da dualidade em programação linear.) Digamos que uma cobertura de A[1 .. n] é um conjunto de subsequências de A[1 .. n] dotada da seguinte propriedade: todo elemento de A[1 .. n] pertence a pelo menos uma das subsequências do conjunto. Se todas as sequências da cobertura forem estritamente decrescentes, temos uma CSSED (cobertura por subsequências estritamente decrescentes). Uma CSSED de A[1 .. n] é mínima se não existe outra CSSED com menos subsequências.
Exemplo D. As sequências (9, 6, 4), (5, 3), (7) e (9, 6, 4) constituem uma CSSED da sequência (9, 5, 6, 3, 9, 6, 4, 7). Esta CSSED prova que não existe uma SSC de comprimento maior que quatro. Mais que isso: como existe uma SSC de tamanho quatro — como (5, 6, 6, 7), por exemplo — a CSSED é mínima e toda SSC de tamanho quatro é máxima.
9 | 5 | 6 | 3 | 9 | 6 | 4 | 7 |
9 | - | 6 | - | - | - | 4 | - |
- | 5 | - | 3 | - | - | - | - |
- | - | - | - | - | - | - | 7 |
- | - | - | - | 9 | 6 | 4 | - |
- | 5 | 6 | - | - | 6 | - | 7 |
É fácil verificar que toda CSSED de A[1 .. n] é pelo menos tão grande quanto qualquer SSC. Portanto, se uma CSSED tem o mesmo tamanho que uma SSC então a CSSED é mínima e a SSC é máxima. Não é muito difícil verificar a seguinte propriedade minimax: para qualquer A[1 .. n],
o comprimento de uma SSC máxima é igual ao tamanho de uma CSSED mínima.
É um ótimo exercício modificar o algoritmo da SSC máxima para que ele devolva também uma CSSED mínima.