LC 0698 [M] Partition to K Equal Sum Subsets - ALawliet/algorithms GitHub Wiki

The time complexity of the 2nd solution is actually O(2^ (k*n)), because if we have K trees stacked on top of each other, the new height of the tree is K * n.
O(k * 2^n) 2^n each for k subsets
I think that the worst time complexitiy is O(k*2^n). Because for each bucket, we have to check whether one specific subset of the nums array can be put into it. But as we have visited some of the numbers, and doing some optimization, like sorting the array or pruning, the time complexitiy will be much smaller than the worst case. Maybe we can get the average time complexity using the amortized analysis, but I have no idea.
if sums[j] == 0:
    break
This sentence is pure magic. Decrease the time from 2300ms to 30 ms!
Without this sentence, we are actually making each bucket unique.
However, it doesn't make sense because all buckets have the same size and they are the same.
If a single number couldn't fit into one bucket, it is a waste of time to put it into the other bucket.
adamzjk also mentioned the reason and it helps.

pass

class Solution:
    def canPartitionKSubsets(self, nums: List[int], k: int) -> bool:
        # TLE optimization
        if sum(nums) % k:
            return False
        nums.sort(reverse=True)
        
        target = sum(nums) / k
        used = [False] * len(nums)
        
        def backtrack(i, k, subsetSum):
            # print(i,k,subsetSum)
            if k == 0:
                return True
            if subsetSum == target:
                return backtrack(0, k - 1, 0)
            
            for i in range(start, len(nums)):
                if used[i] or subsetSum + nums[i] > target:
                    continue
                    
                used[i] = True
                if backtrack(i + 1, k, subsetSum + nums[i]):
                    return True
                used[i] = False

                # TLE optimization
                if subsetSum == 0:
                    break
            return False
    
        return backtrack(0, k, 0)

TLE

def canPartitionKSubsets(self, A, k):
        if len(A) < k:
            return False
        ASum = sum(A)
        A.sort(reverse=True)
        if ASum % k != 0:
            return False
        target = [ASum / k] * k

        def dfs(pos):
            if pos == len(A): return True

            for i in range(k):
                if target[i] >= A[pos]:

                    target[i] -= A[pos]
                    if dfs(pos + 1):
                        return True
                    target[i] += A[pos]

            return False
        return dfs(0)

pass

def canPartitionKSubsets(self, nums, k):
        sums = [0]*k
        subsum = sum(nums) / k
        nums.sort(reverse=True)
        l = len(nums)
        
        def walk(i):
            if i == l:
                return len(set(sums)) == 1

            for j in range(k):
                sums[j] += nums[i]
                if sums[j] <= subsum and walk(i+1):
                    return True
                sums[j] -= nums[i]

                if sums[j] == 0:
                    break
            return False        
        
        return walk(0)