划分为k个相等的子集

 首先我们可以知道有两种情况一定是false的,一种是整个数组的总和不能被k整除,还有一种是数组中的最大值大于均分后的平均值。

其次我们先将数组排序,并且建立一个用来标记每个元素是否已经被使用的bool数组。

接下来就是最重要的回溯算法了。

 

设均分后的每组之和为sum。 

我们以上图的数组为例,第一步先从最大且没被使用的数i开始, 求出i与sum的差值。如果它被使用,则一定能在接下来的数组中找到和为sum-i的元素集合(集合大小未知)。

如果不能找到和为sum-i的集合,就需要将i向前移动一位,重复这个过程直到找到一组符合的元素(true),或者遍历完了数组(false)。

显让这个过程是递归的,递归的边界是i已经小于0,或者当前i就等于当前的sum。

不过要注意的是这个递归也包涵了回溯的循环,需要找遍所有的i,以及它们后面的数组是否存在sum-i的集合,然后才能跳出边界。

class Solution {
public:
    bool canPartitionKSubsets(vector<int>& nums, int k) {
        bool* used=new bool[nums.size()];
        fill(used,used+nums.size(),false);
        sort(nums.begin(),nums.end());
        int sum=0;
        int i,j;
        for(int x:nums)
        sum+=x;
        if(sum%k!=0)
        return false;
        sum=sum/k;
        if(nums[nums.size()-1]>sum)
        return false;
        for(i=0;i<k;i++)
        {
            j=nums.size()-1;
            while(j>=0&&used[j])
            j--;
            if(j<0||!dp(nums,used,sum-nums[j],j-1))
            return false;
            used[j]=true;
        }
        return true;
    }
    bool dp(vector<int>& nums,bool* used,int sum,int start)
    {
        if(sum==0)
        return true;
        int i=start;
        while(i>=0)
        {
            while(i>=0&&(used[i]||nums[i]>sum))
            i--;
            if(i<0)
            return false;
            if(nums[i]==sum)
            {
                used[i]=true;
                return true;
            }
            if(dp(nums,used,sum-nums[i],i-1))
            break;
            else
            i--;
        }
        if(i<0)
            return false;
        used[i]=true;
        return true;
    }
};

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值