LeetCode 698. Partition to K Equal Sum Subsets(深搜剪枝)

给定一个整数数组nums和一个正整数k,检查是否能将数组分成k个子集,使得它们的元素之和相等。文章通过详细解释和示例展示了如何利用深度优先搜索和剪枝策略来解决这个问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目来源:https://leetcode.com/problems/partition-to-k-equal-sum-subsets/

问题描述

698. Partition to K Equal Sum Subsets

Medium

Given an array of integers nums and a positive integer k, find whether it's possible to divide this array into k non-empty subsets whose sums are all equal.

 

Example 1:

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

Output: True

Explanation: It's possible to divide it into 4 subsets (5), (1, 4), (2,3), (2,3) with equal sums.

 

Note:

  • 1 <= k <= len(nums) <= 16.
  • 0 < nums[i] < 10000.

------------------------------------------------------------

题意

给出一个数组nums和一个整数k, 求nums能否被分成k个子集,使得子集中的元素之和相等。

------------------------------------------------------------

思路

深搜剪枝。标答里有好几个剪枝技巧,非常难想到:

1. 最重要的剪枝:由于k个子集是无序的,因此line 29~31的键值可以避免一些如下情况带来的重复,其中一种组合在k个子集进行了全排列。

2. 不算剪枝的加速技巧(次重要):判断是否为true时,如果cur指针移动到0之前,那么就可以返回true了,无需比较targets数组的每一项是否等于target. 这是因为line 22已经限定了targets的值永远 <= target,一旦所有nums数组中的数用完,说明targets各项必须等于target

3. 对nums进行排序,过滤掉nums中大数超过target或等于target的情况,并从大到小尝试放入targets,提前过滤掉nums中元素大小不均衡的情形

------------------------------------------------------------

代码

class Solution {
    // for debug
    private void printArray(int[] arr) {
        for (int item: arr) {
            System.out.print(item + ", ");
        }
        System.out.println();
    }
    
    /**
    * nums: original array
    * cur: current position of pointer in original array
    * targets: sum array of {@code k} subsets
    * target: target sum of each subset
    */
    private boolean dfs(int[] nums, int cur, int[] targets, int target) {
        int n = nums.length, k = targets.length;
        if (cur < 0) {
            return true;
        }
        for (int j=0; j<k; ++j) {
            if (targets[j] + nums[cur] <= target) {
                targets[j] += nums[cur];
                if (dfs(nums, cur-1, targets, target)) {
                    return true;
                }
                targets[j] -= nums[cur];
            }
            if (targets[j] == 0) {
                break;
            }
        }
        return false;
    }
    
    public boolean canPartitionKSubsets(int[] nums, int k) {
        int sum = 0, n = nums.length;
        if (n < k || k == 0) {
            return false;
        }
        for (int num: nums) {
            sum += num;
        }
        if (sum % k != 0) {
            return false;
        }
        int target = sum / k;
        Arrays.sort(nums);
        if (nums[n-1] > target) {
            return false;
        }
        while (nums[--n] == target) {
            --k;
        };
        int[] targets = new int[k];
        return dfs(nums, n, targets, target);
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值