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)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值