Escrito por Caique Paiva e Enzo Dantas
Recap:
- Caso não saiba ou não lembre o que é um grafo, veja a aula de introdução a grafos.
Definições:
Uma árvore é um grafo sem ciclos e conexo. Veja um exemplo de uma árvore:
O legal das árvores é que elas tem várias propiedades interessantes. Por exemplo, se uma árvore tem vértices, então ela tem arestas. No final do texto tem uma prova mais detalhada, porém o foco dessa aula não é provar esses lemas.
Podemos definir uma árvore por meio de pais e filhos (Também chamados de ancestrais e descendentes). Escolhemos um vértice para ser a raiz, então, os vértices adjacentes a raiz vão ser os filhos da raiz, e os vértices adjacentes a esses filhos vão ser os filhos dos filhos da raiz, e assim vai. Além disso, se subirmos nessa árvore, dizemos que um vértice é pai de se é filho de .
Por exemplo, no grafo acima, é a raiz, os filhos de são , ou seja, o pai de é o vértice , o pai de é o , o pai do e do é o , e o pai do é o .
Além disso, a altura de um vértice é a distância de um vértice para a raiz. Dependendo de por onde você enraiza o grafo,os valores dos pais, dos filhos, e das alturas vão mudar também. Por exemplo, as alturas do grafo acima são:
Uma sub-árvore de um vértice são os filhos de , os filhos dos filhos de , e assim vai até acabar todos os vértices. Por exemplo, a sub-árvore de são os vértices .
Uma folha de uma árvore é um vértice sem filhos. Por exemplo, as folhas da árvore acima são os vértices .
Um detalhe importante é que a árvore facilita muitas coisas que normalmente não podem ser feitas em grafos gerais. Um grafo com ciclos, por exemplo, não permitiria uma tal que a de um vértice depende diretamente da dos vértices adjacentes, pois ficaríamos girando em algum ciclo infinitamente*. Como uma árvore não tem ciclos, isso não é um problema, e é comum a ocorrência de problemas em que a de um vértice depende da dp de seus filhos. Vamos resolver uma questão para deixar a ideia mais clara.
*Talvez contra-intuitivamente, algoritmos de menor caminho são dp's, mas eles conseguem contornar esse problema utilizando uma ideia gulosa esperta. Caso queira entender melhor, veja a aula de menor caminho.
Problema
Dado uma estrutura de uma empresa, sua tarefa é calcular para cara funcionário o número de subordinados.
Input
A primeira linha tem um inteiro : O número de funcionários. Os funcionários estão numerados em e o funcionario é o diretor geral da empresa.
Depois disso, tem inteiros: para cada funcionário seu chefe direto na compania.
Output
Imprima inteiros: para cara funcionário , o número de subordinados.
Constantes
Exemplo:
Exemplo:
Entrada | Saída |
5 1 1 2 3 |
4 1 1 0 0 |
Para submeter o seu código, use esse link.
O problema nos dá a estrutura de uma árvore (N vértices, N-1 arestas, sem ciclos). Com isso, tente pensar em uma dp nessa árvore, com os casos bases sendo os filhos e a transição sendo do pai para os filhos dele.
Seja uma dp que calcula a quantidade de vértices na sub-árvore de (O grafo está enraizado no vértice 1, pois ele não tem chefes). Para uma folha, a quantidade de subordinados que ele tem é 0, então a (onde é uma folha) é 1. Além disso, a resposta do nosso problema vai ser (Pois conta também o próprio ). Então, a transição da vai ser a seguinte:
Então, no final basta retornar todas as 's em ordem.
Agora, a parte mais importante desse problema definitivamente é o código, pois isso ensina uma nova forma de fazer uma dfs!
Ao invés de fazer um mark, vamos fazer dfs(int , int ), e vai ser o pai de . Então, no for para descer nos filhos, faça um if para ver se - se sim, ignore esse vértice e continue o for. Isso ajuda a simplificar o código, o que é sempre bem vindo.
Segue o código para melhor entendimento. Como é o primeiro problema, é recomendado que você leia o código, entenda, feche-o e tente fazer o seu próprio.
Problemas Propostos:
Introdutórios:
Journey
Linear LCA
Tree Distances II
Desafios:
Tree Diameter
Tree Matching
Lema: Se uma árvore tem vértices, então ela tem arestas.
Imagine que temos N árvores isoladas. Adicionemos uma nova aresta que conecta duas árvores previamente não conectadas. Por definição, nenhuma das duas árvores tem ciclo, e ao adicionar essa nova aresta, nenhum ciclo é criado (isso só aconteceria caso fosse criada uma aresta entre dois vértices que já estavam conectados). Logo, o novo grafo é uma árvore pois é conexo e sem ciclos. Perceba também que cada operação de juntar duas árvore diminui em exatamente 1 o número de componentes isoladas, então faremos essa operação exatamente N-1 vezes para obter exatamente 1 componente.
Por fim, perceba que um vértice isolado é uma árvore (pois ele é conexo e sem ciclos) e que ele possui k=1 vértices e k-1=0 arestas. Logo, para se construir uma grafo conexo sem ciclos a partir de um conjunto de N vértices isolados, usamos exatamente N-1 arestas.
Caso queira ver mais teoremas matemáticos (e suas provas) sobre árvores e grafos em geral, o noic de matemática tem um material interessante para você!