Aula por Arthur Lobo
Conhecimento prévio necessário:
Assim como podemos usar DP (Dynamic Programming) para resolver problemas em sequências lineares (como o problema do troco), também podemos usá-las para resolver problemas em árvores.
Nos problemas de Programação Dinâmica em árvore, geralmente vamos enraizar a árvore em algum vértice arbitrário (comummente escolhemos o 1) e a DP vai guardar informações sobre cada subárvore do grafo. Ou seja, vai guardar alguma informação sobre a subárvore de , mas normalmente vão ter mais estados do que apenas qual a subárvore. Mas considerando que existe apenas um estado (a raiz da subárvore), vai ser nesse formato:
- Caso base: Toda DP precisa ter um caso base. No caso de DP em árvore esse caso vão ser as folhas (vértices que não tem filhos). Sempre conseguimos calcular o valor de em se for uma folha.
- Transição: Para calcular o valor de , vamos primeiro ter que calcular o valor da de todos os filhos de , e então calcular baseado nesses valores. A exata transição sempre vai depender do problema.
- Resposta: Depois de calcular todas as , a resposta do problema geralmente vai ser (ou 1, se raiz for 1). Mas assim como a transição, isso vai depender do problema.
A ideia é que vai guardar alguma informação se estivermos olhando para uma árvore é que apenas a subárvore de e, com a informação das subárvores dos filhos de , podemos calcular , fazendo isso das folhas até chegar na raiz.
Obs: Quando se referimos à subárvore de em uma árvore enraizada, estamos nos referindo à todo vértice que passa por no caminho até a raiz (incluindo o próprio ).
Motivação Inicial
Vamos imaginar o seguinte problema:
Dado uma árvore de vértices e arestas, vértice () tem moedas. Você pode escolher um conjunto de vértices nessa árvore e pegar as moedas dos vértices nesse conjunto, mas tem um detalhe: não podem existir dois vértices adjacentes no conjunto. Diga qual é a maior soma de moedas de um conjunto válido.
Formalmente, qual o maior valor da soma tal que é um subconjunto de vértices da árvore e não existem dois vértices adjacentes (ou seja, que são diretamente ligados por uma arestas) presentes nesse conjunto. Vale ressaltar que não é um valor fixo, apenas o tamanho do conjunto.
Clique aqui para conferir o enunciado inteiro
Solução
A ideia nesse problema é fazermos uma DP em árvore com 2 estados: Um vai ser falando qual subárvore estamos vendo e o outro diz se a raiz está no conjunto escolhido ou não. Então a será:
- = maior soma olhando apenas para a subárvore de e não é um dos vértices escolhidos. Ou seja, esse é o maior valor da soma , tal que é um subconjunto de vértices da subárvore de e não está nesse subconjunto.
- = maior soma olhando apenas para a subárvore de e é um dos vértices escolhidos. Ou seja, esse é o maior valor da soma , tal que é um subconjunto de vértices da subárvore de e está nesse subconjunto.
Pare por alguns instantes e tente imaginar como seriam os casos base, as transições e a resposta dessa DP. Tentou? Então vamos lá:
Primeiro vamos imaginar para :
- Caso base: Como falado a cima, os casos bases da nossa DP serão as folhas. Se é uma folha e não escolhemos ele para o subconjunto, então .
- Transição: Primeiro temos que calcular o valor das DP todos os filhos de . Depois de fazermos isso, vamos olhar para cada filho de . Já que o não foi escolhido para estar no subconjunto, o pode estar ou não nele, então podemos pegar o melhor valor entre e . Ou seja, se o não foi escolhido para estar no subconjunto, então todas as subárvores dos filhos dele serão livres para ter ou não a raiz escolhida. Formalmente, .
Agora para :
- Caso base: Se é uma folha e escolhemos ele para o subconjunto, então .
- Transição: Depois de calcularmos as DP dos filhos, vamos olhar para cada filho de . Já que o foi escolhido para estar no subconjunto, o não pode estar nele (pois é adjacente à ), então somos obrigados a pegar . Ou seja, se o foi escolhido para estar no subconjunto, então todas as subárvores dos filhos dele serão obrigadas a não escolherem a raiz. Além disso, já que escolhemos a raiz, temos que adicionar . Formalmente, .
Lembrando que temos que calcular ambas as DP ao mesmo tempo, então na vamos calcular e . Agora a resposta será simplesmente o maior valor entre colocar a raiz (provavelmente o 1) e não colocá-lo. Ou seja .
Código: