Leetcode 823. 요인이 있는 이진 트리 [솔루션]

이 질문은 동적 프로그래밍 및 순열 조합에 대한 지식을 확인합니다. 저도 초심자라 인터넷에서 답을 찾아봐야 이해했는데 일단 이해하고 나면 앞으로 그런 질문들을 많이 이해하는데 도움이 됩니다.

난이도: 보통
이동:
  • Problem Statement
  • Explanation
  • Solution
  • Implementation

  • 문제 설명

    Question: https://leetcode.com/problems/binary-trees-with-factors/

    고유한 정수 배열 arr 이 주어지면 각 정수 arr[i] 는 엄격하게 1 보다 큽니다.

    우리는 이러한 정수를 사용하여 이진 트리를 만들고 각 숫자는 여러 번 사용할 수 있습니다. 리프가 아닌 각 노드의 값은 자식 값의 곱과 같아야 합니다.

    우리가 만들 수 있는 이진 트리의 수를 반환합니다. 답이 너무 클 수 있으므로 답을 모듈로109 + 7로 반환합니다.

    예 1:

    입력: arr = [2,4]
    출력: 3
    설명: 우리는 다음과 같은 나무를 만들 수 있습니다: [2], [4], [4, 2, 2]
    예 2:

    입력: arr = [2,4,5,10]
    출력: 7
    설명: 다음 나무를 만들 수 있습니다: [2], [4], [5], [10], [4, 2, 2], [10, 2, 5], [10, 5, 2] .

    제약:
  • 1 <= arr.length <= 1000
  • 2 <= arr[i] <= 109

  • 설명

    First, we need to understand, what is a factor to a number. For a number n , x is a factor if n%x==0 so two numbers x and y can be children of n in the binary tree, only if x * y = n .

    One small point to bring into notice: It's given that 2 <= arr[i] because if arr[i] is 0 or 1 then infinite solutions are possible.

    So we need to find that how many such trees of any depth can be created using a given number in any quantity. for example, if given numbers are [2,4,5,10] then 7 trees are possible as below:

    [2]           [4]           [5]           [10]
    
         [4]              [10]              [10]
        /   \            /    \            /    \
      [2]   [2]        [5]    [2]        [2]    [5]
    

    해결책

    We need to see this problem as a problem breakable into subproblems, like in the example [2,5,10,20] we can see this problem broken into subproblems [2] , [2,5] , [2,5,10] and then final problem [2,5,10,20] . We will solve these subproblems in order and by finding the number of trees with an item as root which is just added in the current subproblem in comparison to the last subproblem i.e.

    1. We'll first find the number of trees with 2 as a root and no children options available i.e. 1 .
    2. Then the number of trees with 5 as a root and [2] is available as children option i.e. 1 .
    3. Then the number of trees with 10 as a root and [2,5] is available as children option i.e. 3 .
    4. Then the number of trees with 20 as a root and [2,5,10] is available as children option i.e. 7 .

    All the trees possible are as below:



    결국 우리는 문제에 대한 해결책을 얻기 위해 위의 트리 수를 모두 합산합니다. 우리는 동적 프로그래밍을 사용할 것입니다.

    각 하위 문제에 대한 답을 얻기 위해 우리는 1 트리가 해당 요소만으로 가능하다는 논리를 사용하여 루트가 유일한 트리인 n를 가정합니다. 그리고 n의 인수인 각 요소에 대해 x라고 가정해 봅시다(그리고 다른 하나는 y입니다) 우리는 x의 하위 솔루션과 y의 하위 솔루션의 곱셈을 추가할 것입니다. 왼쪽 자식으로 한 번x, 왼쪽 자식으로y. 요소는 항상 실제 숫자보다 작기 때문에 배열을 먼저 정렬하므로 매번 전체 배열에서 요소를 찾을 필요가 없습니다. 현재 숫자가 정렬된 배열이 되기 전에 요소만 볼 수 있습니다.

    DP 방정식:

    dp[i] = sum(dp[j] * dp[i / j]) 
    ans = sum(dp[i])
    

    코드를 보자. 더 명확해질 것이다.

    구현

    C++ Code:

    #define MOD 1000000007
    class Solution {
    public:
        int numFactoredBinaryTrees(vector<int>& arr) {
            unordered_map<int,long int> dp;
            int len=arr.size();
    
            // Sorting array
            sort(arr.begin(), arr.end());
    
            for(int i=0;i<len;i++){
                // One tree is always possible (root only)
                dp[arr[i]] = 1;
    
                // Check for all elements less than current element arr[i]
                for(int j=0; j<i; j++){
                    // Check if arr[j] is factor of arr[i] 
                    // and the second factor (arr[i]/arr[j]) is seen
                    if(arr[i]%arr[j]==0 && dp.find((arr[i]/arr[j]))!=dp.end()){
                        // Add combinations in dp with arr[j] as left child
                        // So (arr[i]/arr[j]) becomes right child automatically
                        dp[arr[i]] += (dp[arr[j]] * dp[(arr[i]/arr[j])]) % MOD;
                        dp[arr[i]] %= MOD;
                    }
                }
            }
    
            int ans=0;
            // Find sum of dp to sum solution of all subproblems
            for(auto i : dp){
                ans += i.second;
                ans %= MOD;
            }
            return ans;
        }
    };
    
    • Time Complexity: O(N^2) , where N is the length of arr . This comes from the two for-loops iterating i and j .
    • Space Complexity: O(N) , the space used by dp .

    Runnable C++ code:

    좋은 웹페이지 즐겨찾기