day01 leetcode 698 劃分爲k個相等的子集 [中等]

題目描述:leetcode 698 劃分爲k個相等的子集

給定一個整數數組  nums 和一個正整數 k,找出是否有可能把這個數組分成 k 個非空子集,其總和都相等。

示例 1:

輸入: nums = [4, 3, 2, 3, 5, 2, 1], k = 4
輸出: True
說明: 有可能將其分成 4 個子集(5),(1,4),(2,3),(2,3)等於總和。
 

注意:

1 <= k <= len(nums) <= 16
0 < nums[i] < 10000

 

解題思路:

1.典型的遞歸可以解決的問題,對於某個子集,其差集執行類似邏輯即可

2.求子集是一個慣用方法

3.要想基本ac不超時,加入剪枝方法 sum(nums) / k,我表示此trick沒有想到,看官方題解想到的,還是腦子不活啊

4.自己面對部分用例超時的時候,進行了提前求和判斷的剪枝技巧,但是沒有啥效果,或者說剪枝力度不夠,遠沒有上面的trick有效果

5.具體詳見下面代碼,時間複雜度不好具體分析,因爲有各種剪枝限制,但基本的粗估複雜度是O(k * 2^n),一共k步,每一步都涉及到了求子集的情況;空間複雜度也是類似道理,每一步都有數組來存儲所有滿足條件的子集和對應差集,O(k * 2^n * n)

 

成績:

執行用時 :772 ms, 在所有 Python 提交中擊敗了11.77%的用戶

內存消耗 :19.4 MB, 在所有 Python 提交中擊敗了33.33%的用戶

 

AC代碼(python):

class Solution(object):
    def canPartitionKSubsets(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: bool
        基本思路:
        1.找所有和爲定值的且長度不大於固定個數的子集
        2.遞歸調用
        """
        # 拷貝數組
        def copy_list(mlist):
            newlist = []
            for value in mlist:
                newlist.append(value)
            return newlist

        # 獲取數組差集
        def get_remain(nums, one):
            one = copy_list(one)
            remain = []
            for num in nums:
                is_find = False
                for idx, m in enumerate(one):
                    if num == m:
                        del one[idx]
                        is_find = True
                        break
                if not is_find:
                    remain.append(num)
            return remain

        # 查找滿足指定和且長度不超過max_len的子集和對應差集數組
        def find_subs(nums, need_sum, max_len):
            result = []
            subs = []
            for idx, num in enumerate(nums):
                if idx == 0:
                    newlist = [num]
                    if len(newlist) <= max_len:
                        if sum(newlist) == need_sum:
                            result.append(newlist)
                        if len(newlist) < max_len:
                            subs.append(newlist)
                else:
                    mlen = len(subs)
                    for idx in range(mlen):
                        newlist = copy_list(subs[idx])
                        newlist.append(num)
                        if len(newlist) <= max_len:
                            if sum(newlist) == need_sum:
                                result.append(newlist)
                            if len(newlist) < max_len:
                                subs.append(newlist)
            real_result = []
            for one in result:
                real_result.append((one, get_remain(nums, one)))
            return real_result


        # !!! 題目trick 重要的剪枝方法 sum(nums) / k 沒有這個邏輯,部分用例超時
        need_sum = sum(nums) / k

        # 遞歸查找的方法,返回是否可分割爲k個且和均爲need_num的子集
        def mfind(nums, k):
            if len(nums) < k:
                return False
            if k == 0:
                if len(nums) == 0:
                    return True
                return False
            
            if sum(nums) % k != 0:
                return False
            subs = find_subs(nums, need_sum, len(nums)-k+1)
            for sub, remain in subs:
                if mfind(remain, k-1):
                    return True
            return False
        return mfind(nums, k)

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章