leetcode 题:698. 划分为k个相等的子集(中等)

一、题目描述: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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/partition-to-k-equal-sum-subsets
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

二、解题思路

一开始以为是动态规划解,想了半天没想出好办法,使用递归解决了

1、计算出数组列和sum,如果不能是k的倍数的话。return false;

2、计算出avg = sum/k,分成k份,平均每份的数量agv。

3、对数组进行升序排序,排序的目的是为了减少不必要的递归操作,当累加值大于avg的话,说明后面的值加了也会大于avg值,没必要再进行计算,直接返回就可以。

4、递归

a)边界条件:当找到k个累加值为avg的子数组。return 2(true)

b)//递归遍历寻找子数组,使用tag(set)标记已经遍历过的数字(如果数字已经遍历过则跳过),path 记录当前找到的子数组(相当于栈,如果累加值sum小于agv,数字入栈,打标记,如果累加值sum大于avg,数字出栈,标记取消。如果累加值==avg,说明找到了子数组,标记不取消,并且sum清0,定义下一子数组(初始值为0),从头开始找sum == avg的子数组)

c)上面递归有很多不必要的操作,比如当我们从头遍历第i个子数组的时候,找到第一个未遍历过的数字,这个数字如果没找到符合要求的子数组(不能找到一个子数组和为avg),剩下的数字不能找到第i个符合要求的子数组,不能找到后面的递归都没必要走了。所以在这里可以直接返回。回溯到上面的第i-1个子数组,退一步,进入下一轮递归。

三、C++代码

class Solution {
public:
    bool canPartitionKSubsets(vector<int>& nums, int k) {
        long long sum = sums(nums);
        int len = nums.size();
        if (k == 0 || len == 0)
            return false;
        if (sum%k != 0)
            return false;
        int avg = sum/k;
        cout<<"avg="<<avg<<endl;
        sort(nums.begin(),nums.end());
        vector<int> path;
        set<int> tag;
        int is_find = dfs(nums,0,k,0,path,tag,avg);
        if(is_find == 2)
            return true;
        return false;
        
    }
    void printt(vector<int>&nums,vector<int>& path,set<int> &tag)
    {
        cout<<"print_path:";
        int sum = 0;
        for(int i = 0;i < path.size();i++)
        {
            cout<<"i="<<path[i]<<"num="<<nums[path[i]]<<",";
            sum+=nums[path[i]];
        }
        cout<<"sum_path="<<sum<<endl;
        cout<<"print_set:";
        sum = 0;
        for(auto it = tag.begin();it != tag.end();it++)
        {
            cout<<nums[*it]<<",";
            sum+=nums[*it];
        }
        cout<<"sum_set="<<sum<<endl;
        
    }
   int dfs(vector<int>& nums,int sum,int k,int index,vector<int> & path,set<int> & tag,int& avg)
    {
       int is_find = -1;
//边界条件:当找到k个累加值为avg的子数组。说明找到了第k个sum == avg的子数组。总数组可以切分成k个和相同的子数组。直接返回。
       if(tag.size() == nums.size() && k == 1 && sum == avg )
           return 2;
//如果sum>avg,说明这个子数组不服符合要求,并且后面的子数组的累加值也会大于agv。所以直接返回
       if(sum>avg)
           return -1;
//递归遍历寻找子数组
       for(;index<nums.size();index++)
       {
       if(tag.find(index) != tag.end())
               continue;
        if(sum+nums[index]> avg)
        {
               return -1;
        }
        path.push_back(index);
        tag.insert(index);
//sum == avg的时候,说明找到了符合要求的子数组,开始从头开始找下一个子数组,所以sum清0,index从0开始。路径path清理。标志tag保留
        if(sum+nums[index] == avg)
        {
//边界条件:k == 1的时候,并且这时累加值sum == avg了,说明找到了第k个sum == avg的子数组。总数组可以切分成k个和相同的子数组。直接返回
        if(k == 1)
            return 2;
            //printt(nums,path,tag);
            vector<int> path_t;

        is_find = dfs(nums,0,k-1,0,path_t,tag,avg);

        if (is_find == 2)
            return 2;
        path.pop_back();
        tag.erase(index);
        }
        else
        {
        is_find = dfs(nums,sum+nums[index],k,index+1,path,tag,avg);
         if(is_find == -1)
         {
            path.pop_back();
            tag.erase(index);
//这里比较重要,这里判断未标识的第一数字,未找到符合要求的子数组,直接退出循环,没必要再为第二个及以后的未遍历过的数字寻找子数组了,因为在这里已经不符合要求了。
            if(path.size() == 0)
                break;
         }
          if(is_find == 2)
              return 2;
       }
      }
       return is_find;


}
    long long sums(vector<int>&nums){
        long long sumss = 0;
        for (int i = 0 ;i < nums.size();i++)
        {
            sumss+=nums[i];
        }
        return sumss;
    }
};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值