leetcode 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

想不出来什么好的办法,看看网上 吧。

你看,既然每个子集的和都是相等的,那么,就去凑好了。另外就是剪枝,剪枝是重要的:

在所有预备工作完成后,你如何进行递归呢?如果可以递归的话,你需要首先定义这个递归函数是干什么用的,而所有的递归,都是一个树状结构,递归到最后,就是一个解,就是这样。所以我递归就是,如果不是最后的解,我就接着递归下去,如果是最后的解,就进行判断,对就是这样。

自己写的代码总是不通过,也不知道是怎么回事,Line 17: Char 10: runtime error: load of value 253, which is not a valid value for type 'bool' (__Serializer__.cpp)

class Solution {
    int l_n; // 数组长度
    int l_k; // 一个子集的总和
    int l_m; // 划分子集个数
    vector<vector<int>> l_res; // 结果判断数组
public:
    // 递归到最后进行判断
    bool function(vector<int>& nums, int c_i){
        bool res; // 作为未递归到最后时的结果
        // 如果到了最后,进行判断
        if(c_i == l_n){
            for(int i=0; i<l_m; i++){ // 一共m个子集
                int sum = 0; // 子集的和
                cout<<i<<":";
                for(int j=0; j<l_res[i].size(); j++){
                    sum += l_res[i][j];
                    cout<<l_res[i][j]<<" ";
                }
                cout<<endl;
                if(sum != l_k) return false; // 如果有一个不是就返回false
            }
            cout<<"\n_____________\n";
            return true;
        }
        else{ // 如果不是最后的,就继续向下递归,就是随便插入一个,最后取出
            for(int i=0; i<l_m; i++){
                l_res[i].push_back(nums[c_i]);
                res |= function(nums, c_i+1); // 递归到下一层并输出结果
                l_res[i].pop_back();
            }
        }
        return res;
    }
    bool canPartitionKSubsets(vector<int>& nums, int c_m) {
        l_m = c_m; // 设定初始变量
        l_n = nums.size();
        if(l_n == 0) return false; // 如果长度为0返回false
        int max = nums[0]; // 最大值
        int sum = 0; // 数组总和
        for(int i=0; i<l_n; i++){ // 进行总和计算和最大值获取
            sum += nums[i];
            if(max < nums[i]) max = nums[i];
        }
        if((sum%l_m) != 0) return false; // 如果每一个的总和不是整数自然不行
        l_k = sum/l_m; 
        if(max > l_k) return false; // 如果最大元素还大于子集总和自然不行
        for(int i=0; i<l_m; i++) l_res.push_back({}); // 初始化res数组
        // 下面进行递归
        return function(nums, 0);
    }
};

下面是网上的做法,写的真好,或者说,思想真好,只有成功才执行下一个递归,这种方法值得借鉴,套娃式递归。
 

class Solution {
    bool backtracking(vector<int> &nums, int k, int target, int cur, int start, bool* used) {
        // 返回条件
        if (k == 0) return true; // 这个k就是划分的多少个集合
        if (cur == target) { // target就是一个子集合多大
            // 如果本集合成功,构建下一个集合,下面的就不管了
            return backtracking(nums, k-1, target, 0, 0, used);
        }
        for (int i = start; i < nums.size(); i++) { // 从start开始到末尾
            if (!used[i] && cur+nums[i] <= target) {// 如果没有使用并且小于等于
                used[i] = true; // 就使用它
                if (backtracking(nums, k, target, cur+nums[i], i+1, used)) return true;
                used[i] = false; // 回归原位置
            }
        }
        return false;
    }
    
public: 
    bool canPartitionKSubsets(vector<int> nums, int k) {
        // 注意nums[i] > 0
        int sum = 0, maxNum = 0;
        for (int i = 0; i < nums.size(); i++) {
            sum += nums[i];
            if (maxNum < nums[i]) maxNum = nums[i];
        }
        if (sum % k != 0 || maxNum > sum/k) return false; // 常规避险
        bool* used = new bool[nums.size()]; // 一个使用标记变量
        return backtracking(nums, k, sum/k, 0, 0, used);
    }
};

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值