Dada uma árvore T com n nós, quantas subárvores (T') de T têm no máximo K arestas conectadas a (T – T')? Formato de entrada
A primeira linha contém dois inteiros n e K, seguidos por n-1 linhas, cada uma contendo dois inteiros a e b, indicando que há uma aresta entre a e b.
Restrições
1 <= K <= n <= 50 Cada nó é indicado por um número distinto de 1 a n.
Formato de saída
Um único inteiro que denota o número de subárvores possíveis.
def cutTree(n, k, edges):
g = [[] for _ in range(n)]
for i in edges:
g[i[0]-1].append(i[1]-1)
g[i[1]-1].append(i[0]-1)
global ans
ans = 1
def multiply(x, y):
ans = defaultdict(lambda: 0)
for k, v in x.items():
for k1, v1 in y.items(): ans[k+k1-1] += v*v1
for k, v in ans.items():
if k in x: x[k] += v
else: x[k] = v
def dfs(i,p):
global ans
if g[i] == [p]:
ans += 1
return {0:1}
x = 1 if i else 0
res = {len(g[i])-x : 1}
for nxt in g[i]:
if nxt != p: multiply(res, dfs(nxt, i))
ans += sum(((v if i+x <= k else 0) for i, v in res.items()))
return res
dfs(0,-1)
return ans
Ele resolve https://www.hackerrank.com/challenges/cuttree/forum, mas como?
Aqui está uma explicação que posso lhe dar (se precisar de esclarecimentos, avise-me).
Idéia principal
Já que uma subárvore T′ de T é um conjunto conectado de nós.
Imagine estender tal subárvore começando de algum nó e então decidir, para cada nó adjacente (filho na árvore DFS), se estamos anexando-o ou não.
Quando você não anexa um ramo, essa aresta é cortada.
Observações
Programação dinâmica
O código usa programação dinâmica para contar efetivamente o número de diferentes ramificações (sim, apesar do que o membro do fórum/discussão do Hackerrank alega, este código está usando um dp subjacente).
Atualmente, o código cria uma tabela de dp de dicionário onde as chaves são o número de arestas cortadas que a subárvore (que inclui o nó atual) tem, e os valores são o número de maneiras de atingir isso.
Note que a função "multiply" combina estados dp. Por exemplo:
a). Suponha que você tenha uma configuração atual com k (edgeCount1) arestas de contorno (da subárvore parcial do nó atual) e uma subárvore filha que pode ser anexada de maneiras que produzem k1 (edgeCount2) arestas de contorno. (note novamente a observação de que estamos economizando 1 corte)
2. Essa convolução sobre os números possíveis de arestas de contorno pode ser considerada análoga à multiplicação de dois polinômios (onde os expoentes rastreiam o número de arestas de contorno) com um deslocamento de −1.
O que torna esse código complicado
Uma coisa que o autor fez para tornar o código tão difícil de entender é que eles usaram variáveis com nomes ruins e, em alguns casos, nomes que realmente distraíam da ideia principal. Por causa disso, incluí um código atualizado (um que simplifica parte da lógica, mas também nomeia as variáveis de uma forma que torna um pouco mais fácil entender o código):
Minha explicação pode ficar curta (no que diz respeito à simplificação), e eu encorajo outros a postarem suas próprias respostas explicando o código.
Espero que meu código de exemplo com os nomes de variáveis ajustados esclareça um pouco do que o código está fazendo
(embora eu queira observar que removi o caso base no dfs, pois não era necessário... Talvez tenha sido um erro no que diz respeito à explicação (pois pode ser mais fácil ver o outro, ou você pode tentar entender isso também)...
Novamente, eu encorajo outros a postarem suas explicações, sinta-se à vontade para deixar um comentário se você não entender parte dele.