![]() |
Problema C - Apagando Letras |
Tipo de problema: Programação Dinâmica
Autor do Problema: Pedro Ribeiro (DCC/FCUP)
O Problema
Dadas duas palavras, a tua tarefa é determinar qual a melhor
pontuação possível de obter numa sequência de deletes que
transforma a primeira na segunda palavra. A pontuação de cada
delete é igual à soma das distâncias entre a letra apagada e as
duas letras adjacentes.
Restrições
São garantidos os seguintes limites em todos os casos de teste que
irão ser colocados ao programa:
1 ≤ T2 < T1 ≤ 500 | Tamanho das palavras |
Solução Base:
A solução mais simples a considerar requer que pensemos em como
funciona o problema. A ideia é ir retirando letras à primeira
palavra até chegar à segunda. Usando uma função recursiva que
recebe dois parâmetros: uma string e um número. A string
representa a palavra que temos neste momento e o número a
pontuação que obtivemos para chegar a essa string. Agora basta
eliminar cada letra dessa string individualmente, contar a nova
pontuação e fazer uma chamada recursiva com esses novos
valores. Quando a string que temos for igual à segunda palavra
temos uma solução parcial, ou seja, temos de encontrar o máximo
destas
Esta seria uma solução fatorial que daria aproximadamente 40 pontos.
Solução Especial:
Para este passo intermédio vamos considerar os casos onde o
tamanho da segunda palavra é 1. Neste caso apenas nos temos de
preocupar em descobrir a melhor maneira de eliminar tudo até
cada letra igual à letra da segunda palavra e depois dessa
letra. Para isto vamos recorrer a uma solução com Programação
Dinâmica. Para isto vamos considerar uma
matriz remDP[i][j] que representa a melhor maneira de
remover todos os caracteres entre i e j
exclusivé. Para calcular este valor, basta reparar
que remDP[i][j] = max(remDP[i][k] + remDP[k][j] + cost(i, k,
j)), com k a variar entre i e j,
exclusivé. O valor da função custo é de considerar que retiramos
a letra na posição k que tem à sua esquerda a letra na
posição i e à sua direita a letra na
posição j. Reparem que estamos a assumir que os índicies
da palavra começam em 1, pois queremos as soluções que apagam
também o primeiro caracter.
Para perceber porque é que isto funciona basta imaginar o problema ao contrário, a ideia é inserir letras e não remover, que é equivalente. Assim, no cálculo da matriz consideramos todos os índices k intermédios e inserimos essa letra, depois é só adicionar a inserção para cada uma das metades restantes da palavra. É importante a ordem pela qual se constroí a matriz (se for feito de uma maneira Bottom-Up), porque é preciso construir primeiro os remDP[i][j] onde j - i = 1, depois = 2 e por aí fora.
Esta seria uma solução O(N ^ 3) que daria aproximadamente 70 pontos (se para os casos onde T2 é maior que 1 usarmos a solução base).
Solução Ótima:
Para finalmente resolvermos o problema é preciso extender a
solução anterior. Considerando que temos a
matriz remDP[i][j] construida, vamos fazer uma segunda
matriz de Programação Dinâmica que aproveita estes valores. A
ideia é construir uma matriz matDP[i][j] que representa a
melhor solução para a palavra 1 até ao caracter i e da
palavra 2 até ao caracter j. A recorrência é simples,
temos dois casos: se o caracter i da palavra 1 for igual
ao j da palavra 2, então podemos considerar todas as
letras k desde i - 1 a 0 e procurar uma letra que
seja igual ao caracter a seguir a j (que é j - 1)
e remove todas as letras nesse intervalo, a solução para este
caso é asssim: max(remDP[k][i] + matDP[k][j - 1])
[atenção aos limites nas matrizes, o objetivo não é eliminar o
caracter i, é mantê-lo]. Caso os caracteres forem
diferentes (notem que também é preciso testar estas soluções
para o caso em que os caracteres são iguais, pois só porque são
iguais não significam que fazem parte da solução), a recorrência
é semelhante, mas torna-se apenas encontrar o próximo caracter
na palavra 1 que seja igual ao caracter de índice j na
palavra 2 e remover todos os caracteres desde esse caracter
até i (inclusivé), logo fica: max(remDP[k][i + 1] +
mDP[k][j]) [reparem que aqui já temos i + 1 na
primeira matriz, pois queremos discartar o caracter i].
É apenas preciso testar atenção aos casos extremos, como o j = 0, mas fora isso a solução é simplesmente mat[T1][T2].
Esta seria uma solução O(N ^ 3) que daria aproximadamente 100 pontos.
Ligações interessantes: