leetcode698. 划分为k个相等的子集

  • Post author:
  • Post category:其他



传送门


题目

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


输入

: nums = [4, 3, 2, 3, 5, 2, 1], k = 4

输出

: True


说明

: 有可能将其分成 4 个子集(5),(1,4),(2,3),(2,3)等于总和。


这种找子集的题,先求和,然后除以子集个数,得到每一部分的子集和,然后凑子集和

    int[] bucket; // 放k的子集的和
    public boolean canPartitionKSubsets(int[] nums, int k) {
        if (k == 1) return true; //如果k是1,直接返回true。
        int len = nums.length;
        int sum = 0;
        for (int num : nums) sum += num; // 算出nums的总和。
        if (sum % k != 0) return false; //子集分不出k份,直接false。
        sum /= k;// sum变为每个子集的和。
        Arrays.sort(nums);
        // Arrays.sort(nums, (a, b)->(b - a));//这样降序失败,因为nums里是基本类型int
        /*
            为什么要排序呢,其实不排序这道题也能做对,但是由于时间的关系就t了。
            排序就是为了优化时间,怎么优化呢?
            我们从nums中最大的数开始找,如果最大的数比子集和都要大,
            或者装下它后没到子集和的大小但是装不下nums中最小的值了,
            那么这个nums绝对是false,因为有一个这么大的数在nums里,
            你把它放在哪个子集里都不合适。
        */
        bucket = new int[k];//这个数组里放的是子集和,总共有k个。
    	Arrays.fill(bucket, sum);
        return dfs(k, nums, len - 1);
    }

    public boolean dfs(int k, int[] nums, int cur) {//cur为当前的位置,从最后开始往前走。
        if (cur < 0) return true;// cur走到-1时,说明所有的数全部都放进桶里了。这时一定是true
        for (int i = 0; i < k; i++) {
            //两种可能,这个数正好是桶当前的容量,或者将这个数放进桶后这个桶还能再放别的数。
            // if里也可以写成bucket[i]>=nums[cur] 但是起不到减枝的效果了
            if (bucket[i] == nums[cur] || bucket[i] - nums[cur] >= nums[0]) {
                bucket[i] -= nums[cur];
                if (dfs(k, nums, cur - 1)) return true;
                bucket[i] += nums[cur];//回溯
            }
        }
        return false;
	}

这里讨论一种问题,为什么只要判断cur<0就能说明true,而不需要判断一下bucket数组中的值是否都是0。

有没有可能bucket数组中的数有剩余但是cur已经小于0了呢?


答案是不可能



因为如果cur<0,那么说明nums中的所有数全部都放进去了。

cur一旦有一个递归没下去cur就不可能为-1,只能换其他的情况(下一次for循环)再去试。



版权声明:本文为qq_43778308原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。