1.题目描述
给定一个整数数组 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
2.解题思路
这道题给了我们一个数组nums和一个数字k,问我们该数字能不能分成k个非空子集合,使得每个子集合的和相同。给了k的范围是[1,16],而且数组中的数字都是正数。这跟之前那道 Partition Equal Subset Sum 很类似,但是那道题只让分成两个子集合,所以问题可以转换为是否存在和为整个数组和的一半的子集合,可以用dp来做。但是这道题让求k个和相同的,感觉无法用dp来做,因为就算找出了一个,其余的也需要验证。
这道题我们可以用递归来做,首先我们还是求出数组的所有数字之和sum,首先判断sum是否能整除k,不能整除的话直接返回false。
然后需要一个visited数组来记录哪些数组已经被选中了,然后调用递归函数,我们的目标是组k个子集合,是的每个子集合之和为target = sum/k。我们还需要变量start,表示从数组的某个位置开始查找,curSum为当前子集合之和,在递归函数中,如果k=0,并且len(vis) == len(nums):直接返回true。如果curSum等于target了,那么我们再次调用递归,此时传入k-1,start和curSum都重置为0,因为我们当前又找到了一个和为target的子集合,要开始继续找下一个。否则的话就从start开始遍历数组,如果当前数字已经访问过了则直接跳过,否则标记为已访问。然后调用递归函数,k保持不变,因为还在累加当前的子集合,start传入i+1,curSum传入curSum+nums[i],因为要累加当前的数字,如果递归函数返回true了,则直接返回true。否则就将当前数字重置为未访问的状态继续遍历,参见代码如下:
3.代码实现
class Solution(object):
def dfs(self,nums,target,tmp,begin,k,vis):
if k == 0 and len(vis) == len(nums):
print "jin"
return True
for i in range(begin,len(nums)):
if i not in vis:
if tmp + nums[i]> target:
continue
elif tmp + nums[i] == target:
vis.add(i)
if self.dfs(nums,target,0,0,k-1,vis):
return True
vis.remove(i)
else:
vis.add(i)
if self.dfs(nums,target,tmp + nums[i],i+1,k,vis):
return True
vis.remove(i)
def canPartitionKSubsets(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: bool
"""
s = sum(nums)
if s%k != 0:
return False
target = s/k
vis=set()
flag = False
flag = self.dfs(nums,target,0,0,k,vis)
if flag:
return True
else:
return False
优化:倒序排序
class Solution(object):
def dfs(self,nums,target,tmp,begin,k,vis):
if k == 0 and len(vis) == len(nums):
return True
for i in range(begin,len(nums)):
if i not in vis:
if tmp + nums[i]> target:
continue
elif tmp + nums[i] == target:
vis.add(i)
if self.dfs(nums,target,0,0,k-1,vis):
return True
vis.remove(i)
else:
vis.add(i)
if self.dfs(nums,target,tmp + nums[i],i+1,k,vis):
return True
vis.remove(i)
def canPartitionKSubsets(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: bool
"""
s = sum(nums)
if s%k != 0:
return False
target = s/k
vis=set()
nums.sort(reverse=True)
flag = False
flag = self.dfs(nums,target,0,0,k,vis)
if flag:
return True
else:
return False