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

分析

这个其实是一个np问题,所以他的数据也是只有16,因为我们只能做穷举的方法,使用used数组来记录元素是否用过.但是怎么穷举就有一些门道
我的主体使用了回溯算法的框架细节有很多(为了优化加速)

void backtrace(int now,int kk,int sum,vector<int>& nums,vector<bool>& used)

now是使用的元素,kk是已经完成了子集,在达到k个的时候返回.sum是现在的子集sum到了多少.used记录了哪些元素被使用了

  1. 对于每一个backtrace,第一句话就是

      if(res==true) return; 
    

    这是为了得到答案后快速return,防止有答案了之后还在找

  2. 在nums提前删除一些等于per(sum/k)的项,减少后面枚举的数量,毕竟少一个就可能少一个数量级

    int tmp=0;
       auto it=nums.begin();
       while(it!=nums.end())
       {
         
           if(*it==per){
         
               it=nums.erase(it);
               tmp++;
           }
           else it++;
       }
    
  3. 质的飞跃.使用last_pos来记录枚举的位置,
    如果只是使用前两种优化,那么还是会超时,前两个是我最先想到的,其实只是打酱油的,但是写都写了,还是放上了,这个质的飞跃我其实也早想到了,只不过实践来的晚一点.
    这个可以防止大量的重复遍历,就是比如我 1,2,3,4,5.我使用了1,3,那么我不可能使用2,因为使用2是在1,2,的时候用的,我用到的都应该是3后面的.这里就好像是算组合数,怎样才能不重不漏,就是先选1,再选2,2的组合选完了,选3之后,1,3再选3后面的才不会重复,选不到2,这样可以在一个数组长度内选完一次子集的.所以减少了很多.
    并且和used数组等回溯的架构里,tmp_pos也需要在选和不选之后更新.所以使用一个变量记录,这个结果回溯完后再改回去(当我没说,没用ast_pos=tmp_pos;也过了)

     if(!used[i]&&sum+nums[i]<=per)  {
         last_pos

你可能感兴趣的:(leetcode 698. 划分为k个相等的子集)