본문 바로가기

카테고리 없음

Daily Leetcoding Challenge 377. Combination Sum IV

주말에도 한 문제. 식은 도출했지만... 마무리가 아쉬웠던 문제

https://leetcode.com/problems/combination-sum-iv/

 

Combination Sum IV - LeetCode

Can you solve this real interview question? Combination Sum IV - Given an array of distinct integers nums and a target integer target, return the number of possible combinations that add up to target. The test cases are generated so that the answer can fi

leetcode.com

직관

이 문제에서는 숫자 순서가 다르면 다른 조합으로 인정하기 때문에 주어진 배열로 target을 만들 수 있는 모든 순열을 찾아야 한다. 그리고 nums 배열의 숫자를 반복해서 사용할 수 있는 점도 참고해야 한다. 재귀적으로 문제를 푼다면 target을 만들 수 있는 순열의 수는 target - nums[i] (0 <= i < nums.length)가 0보다 크거나 같을 때 target - nums[i]를 만들 수 있는 순열의 수의 합이 된다.

정리하면, combinationSum4(target) = sum(combinationSum4(target - nums[i]) where target - nums[i] >= 0이다. 그리고 베이스케이스는 combinationSum4(0)이 될 텐데, target의 값으로 0이 주어지지는 않지만, 이 재귀식에서 combinationSum4(0)은 target과 nums[i]의 값이 같은 경우를 의미하고 그때는 1가지 순열뿐이므로 combinationSum4(0)를 1로 정의한다. 

접근법

combinationSum4(target) = sum(combinationSum4(target - nums[i]) where target - nums[i] >= 0 식을 이용해서 재귀적으로 문제를 먼저 풀고, 그 뒤에 메모이제이션(memoization)을 이용해서 top-down 방식으로 문제를 풀어보고, 그 뒤에 bottom-up으로 문제를 풀어본다.

복잡도

nums 배열의 크기를 N, target을 간단히 T라고 하자.

재귀함수로 문제를 푸는 경우 시간 복잡도는 O(N^T)가 된다. 공간 복잡도는 재귀 함수의 스택이 최대 T가 될 수 있으므로 O(T)가 된다.

Top-down 접근법의 경우 부분 문제를 저장하는 공간이 T+1만큼 할당된다. 그리고 재귀함수의 스택도 최대 T가 될 수 있으므로 공간 복잡도는 O(T)다. 그리고 시간 복잡도는 O(N * T)가 된다.

(top down 방식이 메모이제이션을 사용하기 때문에 시간 복잡도가 줄어드는 건 맞는데 직관적으로 이해는 잘 안되는 것 같다.)

코드

재귀

class Solution {
    public int combinationSum4(int[] nums, int target) {
        if (target == 0) {
            return 1;
        }
        int ret = 0;
        for (int i=0; i < nums.length; i++) {
            if (target >= nums[i]) {
                ret += combinationSum4(nums, target - nums[i]);
            }
        }
        return ret;
    }
}

Top-down DP

class Solution {
    public int combinationSum4(int[] nums, int target) {
        int[] dp = new int[target+1];
        Arrays.fill(dp, -1);
        dp[0] = 1;
        return helper(nums, target, dp);
    }

    private int helper(int[] nums, int target, int[] dp) {
        if (dp[target] != -1) {
            return dp[target];
        }

        int ret = 0;
        for(int i=0; i<nums.length; i++) {
            if (target >= nums[i]) {
                ret += helper(nums, target - nums[i], dp);
            }
        }
        dp[target] = ret;
        return dp[target];
    }
}

Bottom-up DP

class Solution {
    public int combinationSum4(int[] nums, int target) {
        int[] dp = new int[target+1];
        dp[0] = 1;
        for(int i=1; i<dp.length; i++) {
            for(int j=0; j<nums.length; j++) {
                if (i - nums[j] >= 0) {
                    dp[i] += dp[i - nums[j]];
                }
            }
        }
        return dp[target];
    }
}