leetcode698.划分为k个相等的子集

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
发布了241 篇原创文章 · 获赞 6 · 访问量 7217

猜你喜欢

转载自blog.csdn.net/qq_28468707/article/details/104462101