AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / coding / Perguntas / 79450153
Accepted
Irina
Irina
Asked: 2025-02-19 12:07:13 +0800 CST2025-02-19 12:07:13 +0800 CST 2025-02-19 12:07:13 +0800 CST

Como funciona esse algoritmo cuttree hackerrank?

  • 772

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?

python
  • 1 1 respostas
  • 46 Views

1 respostas

  • Voted
  1. Best Answer
    Sharp Dev
    2025-02-19T14:20:26+08:002025-02-19T14:20:26+08:00

    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

    1. Quando você anexa uma subárvore de um filho, a aresta que conecta o filho ao nó atual passa de cortada para anexada.
    2. Ao combinar escolhas de ramos diferentes, você precisa subtrair 1 para cada ramo que você anexar (porque você economiza um corte).

    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):

    def cutTree(n, k, edges):
        graph = [[] for _ in range(n)]
        #We are creating an undirected graph from the edges
        for i in edges:
            graph[i[0]-1].append(i[1]-1)
            graph[i[1]-1].append(i[0]-1)
        global ans
        ans = 1
        #previously called multiply
        def combine(dp, dp2):
            counts = Counter()
            for edgeCount1, v in dp.items():
                #k (or edgeCount1) is the edgeCount
                #v is the number of times we can count/achieve that edgeCount
                for edgeCount2, v2 in dp2.items():
                    #You'll remember that y (dp2) is also part of dp
                    #k1 (edgeCount2) is edgeCount
                    #v1 (v2) is the number of times we can count/achieve that edgeCount
                    #we multiply to get the combinations
                    #again the observation is that we save 1 cut edge when combining
                    counts[edgeCount1+edgeCount2-1] += v*v2
            for edgeCount, v in counts.items():
                #add combinations to the edgeCount (k)
                dp[edgeCount]+=v
        def dfs(node,parent):
            global ans
            hasParent = 1 if node else 0
            #x is telling us if we have a parent.
            edgeCount = len(graph[node])-hasParent # Number of edges that aren't the parent.
            dp = Counter()
            dp[edgeCount] = 1 #edges: 1
            for nxt in graph[node]:
                #nxt not being equal to parent makes sure we don't revisit the parent.
                if nxt != parent: 
                    combine(dp, dfs(nxt, node))
            #If i (the edge count) + hasParent (which is 1 if there is a parent, and 0 otherwise)
            #In other words if the total edge count is less than or equal to  k (i.e. at most K edges)
            #We are going to add the number of times that combination happens (v), 
            #We discard edge counts that are too large
            ans += sum(((v if i+hasParent <= k else 0) for i, v in dp.items()))
            return dp
        dfs(0,-1) #start with node 0 parent -1 (no parent)
        return ans
    

    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.

    • 1

relate perguntas

  • Como divido o loop for em 3 quadros de dados individuais?

  • Como verificar se todas as colunas flutuantes em um Pandas DataFrame são aproximadamente iguais ou próximas

  • Como funciona o "load_dataset", já que não está detectando arquivos de exemplo?

  • Por que a comparação de string pandas.eval() retorna False

  • Python tkinter/ ttkboostrap dateentry não funciona quando no estado somente leitura

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Reformatar números, inserindo separadores em posições fixas

    • 6 respostas
  • Marko Smith

    Por que os conceitos do C++20 causam erros de restrição cíclica, enquanto o SFINAE antigo não?

    • 2 respostas
  • Marko Smith

    Problema com extensão desinstalada automaticamente do VScode (tema Material)

    • 2 respostas
  • Marko Smith

    Vue 3: Erro na criação "Identificador esperado, mas encontrado 'import'" [duplicado]

    • 1 respostas
  • Marko Smith

    Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores?

    • 1 respostas
  • Marko Smith

    Como faço para corrigir um erro MODULE_NOT_FOUND para um módulo que não importei manualmente?

    • 6 respostas
  • Marko Smith

    `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso?

    • 3 respostas
  • Marko Smith

    Um programa vazio que não faz nada em C++ precisa de um heap de 204 KB, mas não em C

    • 1 respostas
  • Marko Smith

    PowerBI atualmente quebrado com BigQuery: problema de driver Simba com atualização do Windows

    • 2 respostas
  • Marko Smith

    AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos

    • 1 respostas
  • Martin Hope
    Fantastic Mr Fox Somente o tipo copiável não é aceito na implementação std::vector do MSVC 2025-04-23 06:40:49 +0800 CST
  • Martin Hope
    Howard Hinnant Encontre o próximo dia da semana usando o cronógrafo 2025-04-21 08:30:25 +0800 CST
  • Martin Hope
    Fedor O inicializador de membro do construtor pode incluir a inicialização de outro membro? 2025-04-15 01:01:44 +0800 CST
  • Martin Hope
    Petr Filipský Por que os conceitos do C++20 causam erros de restrição cíclica, enquanto o SFINAE antigo não? 2025-03-23 21:39:40 +0800 CST
  • Martin Hope
    Catskul O C++20 mudou para permitir a conversão de `type(&)[N]` de matriz de limites conhecidos para `type(&)[]` de matriz de limites desconhecidos? 2025-03-04 06:57:53 +0800 CST
  • Martin Hope
    Stefan Pochmann Como/por que {2,3,10} e {x,3,10} com x=2 são ordenados de forma diferente? 2025-01-13 23:24:07 +0800 CST
  • Martin Hope
    Chad Feller O ponto e vírgula agora é opcional em condicionais bash com [[ .. ]] na versão 5.2? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench Por que um traço duplo (--) faz com que esta cláusula MariaDB seja avaliada como verdadeira? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng Por que `dict(id=1, **{'id': 2})` às vezes gera `KeyError: 'id'` em vez de um TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos 2024-03-20 03:12:31 +0800 CST

Hot tag

python javascript c++ c# java typescript sql reactjs html

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve