416. Partition Equal Subset Sum

#Algorithm #Algorithm_Knapsack

416. Partition Equal Subset Sum

1. 문제

1-1. 원문

Given an integer array nums, return true if you can partition the array into two subsets such that the sum of the elements in both subsets is equal or false otherwise.

Example 1:
Input: nums = [1,5,11,5]
Output: true
Explanation: The array can be partitioned as [1, 5, 5] and [11].

Example 2:
Input: nums = [1,2,3,5]
Output: false
Explanation: The array cannot be partitioned into equal sum subsets.

Constraints:

1-2. 내용 번역

배열 nums가 주어졌을 때, nums 내의 수들을 서브셋 내의 수들의 합이 같도록 2개의 서브셋으로 나눌 수 있는지의 여부를 리턴해라.


2. 문제 이해

2-1. 내용 이해

Example1을 예로 설명해보면, nums 내의 숫자들의 합은 1+5+11+5 = 22 이다. nums 내의 숫자들을 2그룹으로 나누어서 각 그룹 안의 수의 합이 11이 될 수 있으면 true, 없으면 false를 리턴해야한다. nums 내의 숫자들을 가지고 [1, 5, 5][11]인 서브셋 2개를 만들 수 있고, 각 서브셋의 합이 11이 되므로 Example1의 결과는 true 가 된다.

2-2. 접근법 생각

가방의 무게는 nums 숫자 총 합의 절반, 물건의 무게는 nums 내의 각 인덱스에 저장된 숫자, 물건의 가치는 따로 없고 가방의 무게가 물건의 무게와 같아질 수 있는지의 여부로 대신한다.

초기 엣지 케이스가 있는데 배열의 크기가 1이면 이것을 합의 절반의 서브셋 둘로 나눌 수 없기 때문에 false이고, nums의 총 합이 2로 나누어떨어지지 않아도 서브셋으로 나눌 수 없기 때문에 false 이다.

2-3. 적용한 풀이

Knapsack BottomUp (Tabulation)


3. 구현

class Solution {
    fun canPartition(nums: IntArray): Boolean {
        return bottomUp(nums)
    }

    fun bottomUp(nums: IntArray): Boolean {
        var halfTotal = 0

        for(num in nums) {
            halfTotal += num
        }

        if (halfTotal%2 == 1 || nums.size == 1) return false
        halfTotal /= 2

		// 배열은 [nums 인덱스 숫자] * [현재 까지 총 합] = (현재 까지 총 합을 이전의 nums를 조합해서 만들어 낼 수 있는가의 여부)
        val memo = Array(nums.size){Array(halfTotal+1){false}}

		// 현재 까지 총 합이 0 이면 nums 인덱스 숫자를 포함시키지 않아도 되므로 항상 true
        for (i in 0..nums.size-1) {
            memo[i][0] = true
        }

		// 인덱스 0은 이중 for문에 끼지 않는게 좋고, 첫 시작이기 때문에 인덱스 0의 숫자에 해당하는 합계에서만 true가 됨
        if (nums[0] <= halfTotal) {
            memo[0][nums[0]] = true
        }

        for (idx in 1..nums.size-1) {
            for (total in 1..halfTotal) {
	            // 이전의 인덱스에서 해당 total이 참이였다면 지금도 참이 된다. || nums의 숫자와 total이 같으면 참이 된다.
                memo[idx][total] = memo[idx-1][total] || (nums[idx] == total)

				// nums의 숫자가 total 보다 크면 그 전의 인덱스에서 total-nums의 숫자에 해당하는 결과가 참이였는지를 보면 된다.
                if (total-nums[idx] >= 0) {
                    memo[idx][total] = memo[idx][total] || memo[idx-1][total-nums[idx]]
                }
            }
        }

        return memo[nums.size-1][halfTotal]
    }
}

4. 복잡도