LeetCode 698. Partition to K Equal Sum Subsets(划分为K个相等的子集)

题目描述:

Given an array of integers nums and a positive integer k, find whether it's possible to divide this array into k non-empty subsets whose sums are all equal.

 

给定一个整数数组  nums 和一个正整数 k,找出是否有可能把这个数组分成 k 个非空子集,其总和都相等。

 

Example 1:

Input: nums = [4, 3, 2, 3, 5, 2, 1], k = 4
Output: True
Explanation: It's possible to divide it into 4 subsets (5), (1, 4), (2,3), (2,3) with equal sums.

 

示例 1:

输入: nums = [4, 3, 2, 3, 5, 2, 1], k = 4
输出: True
说明: 有可能将其分成 4 个子集(5),(1,4),(2,3),(2,3)等于总和。

 

Note:

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

 

思路:

该题目可以用深度优先搜索算法。

先计算出子序列的目标序列和target,即用数组nums的总和除以k,如果余数不为0,说明nums不可能被分成k个总和相等的非空子集,返回false。

否则,从nums数组的第一个元素开始,寻找加和等于target的元素的组合,组合中的元素被标记,不参与下一轮递归的选取;当选出一组子序列后,进入下一轮递归,重新从nums数组的第一个元素开始,从未被标记的元素中继续寻找符合要求的子序列。

如果本轮递归无法找到目标和为target的子序列,原因有两个,一是nums数组本来就无法满意题意,二是应该构成本轮递归子序列的元素已经在之前的某一轮递归中被使用,因此需要回溯重新构造之前的子序列。

直到所有的元素被标记,并且找到了k个加和等于target子序列,才返回true,其余的情况都返回false。

 

实现(C++):

class Solution {
    public:
    bool canPartitionKSubsets(vector<int>& nums, int k) {
        if(k==1)
          return true;  
        
        if(k>nums.size())
            return false;
        
        int sum=0;        
        for(int i=0; i<nums.size(); i++)
            sum+=nums[i];
        
        if(sum%k)
            return false;
           
        int target=sum/k; //子序列和的目标和
        int visited[nums.size()]; //访问标记数组
        memset(visited, 0, sizeof(int)*nums.size()); //visited数组的元素都初始化为0
        return dfs(nums,0,0,target,0,k,visited); //深度优先搜索,返回判断结果
    }
    
    public:
    bool dfs(vector<int>& nums, int start, int n, int target, int sub_sum, int             k, int visited[])
    {   
        //nums    nums数组
        //start   本次深搜的起始位置
        //n       记录被访问元素的个数
        //target  子序列的目标和
        //sub_sum 当前的子序列和
        //k       当前还差k个目标和为target的子序列未被找到
        //visited 访问标记数组

        if(k==0&&n==nums.size())
        //如果已经凑齐所有目标和为target的子序列,并且nums数组的所有元素都被标记,返回true 
            return true;

        if(sub_sum==target)
        //如果找到一个和为target的子序列,则当前还差k-1个目标和为target的子序列未被找到,需要重新从nums数组的第一个元素开始,继续从未被标记的元素中寻找下一个目标和为target子序列 
            return dfs(nums,0,n,target,0,k-1,visited);  
        
        for(int i=start;i<nums.size();i++)
        {
            if(visited[i]==0) 
            {
                 if(sub_sum+nums[i]<=target){ 
                     visited[i]=1;  
                    
                     n=n+1;   
                 
                     //下一次递归从当前元素的下一个元素开始搜索,即start=i+1          
                     return dfs(nums,i+1,n,target,sub_sum+nums[i],k,visited);
  
                     n=n-1; //回溯
                     visited[i]=0;  
                }
            }
         }
        return false;
    }
};

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值