给定一个整数数组 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)