1. 程式人生 > >LeetCode 698 Partition to K Equal Sum Subsets

LeetCode 698 Partition to K Equal Sum Subsets

this pro 增加 pos code 有一種 hashset += problem

Problem Description: 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.

輸入是一個整型數組和一個正整數k,判斷是否能將正整數組中的元素分組到k個非空子集合並保持每個子集合中元素的和相等。

Note:

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

解題思路:

此題看似跟416. Partition Equal Subset Sum接近,但並不能用類似的思路來解題。從數據的規模來看,采用深度優先搜索來暴力搜索是可行的。

看了hint後想到的是創建一個大小為k的int[] subsets數組,然後順序考慮每一個nums中元素,對於每一個元素考慮將其加到subsets中的每一個子集合中。對於nums中每一個數,均有k種選擇,此種解法的時間復雜度為O(k^n)。

此種算法可進行一些優化(條件見代碼),代碼如下

class Solution {
    public boolean canPartitionKSubsets(int
[] nums, int k) { int sum = 0; for(int num : nums) sum += num; if(sum % k != 0) return false; int S = sum / k; int[] subsets = new int[k]; return dfs(nums, subsets, 0, S); } public boolean dfs(int[] nums, int[] subsets, int index, int tgt) {
if(index == nums.length) { for(int i = 0; i < subsets.length; i++) { if(subsets[i] != tgt) return false; } return true; } boolean res = false; Set<Integer> set = new HashSet<>(); for(int i = 0; i < subsets.length; i++) { if(tgt - subsets[i] >= nums[index] && set.add(subsets[i])) { //2個減枝條件,1:某個集合加入nums[i]後大於tgt。2:nums相同的元素僅考慮一次 subsets[i] += nums[index]; res |= dfs(nums, subsets, index + 1, tgt); subsets[i] -= nums[index]; } } return res; } }

提交後順利ac,運行時間858ms。若不優化,則TLE。

看了一下discuss caihao0727mail 的解答,總結一下思路:該博主做題的時候沒有加nums元素為正數的條件,所以增加了cur_num,針對sum為0的情況,對於更新後的題目可以省去。

nums中的元素分成k份,只有一種分法(元素值相同即認為相同)-> 每次找到一組子集和為target就開始找下一個子集,直到k為1。

博主的解答如下:

public boolean canPartitionKSubsets(int[] nums, int k) {
        int sum = 0;
        for(int num:nums)sum += num;
        if(k <= 0 || sum%k != 0)return false;
        int[] visited = new int[nums.length];
        return canPartition(nums, visited, 0, k, 0, 0, sum/k);
    }
    
    public boolean canPartition(int[] nums, int[] visited, int start_index, int k, int cur_sum, int cur_num, int target){
        if(k==1)return true;
        if(cur_sum == target && cur_num>0)return canPartition(nums, visited, 0, k-1, 0, 0, target);
        for(int i = start_index; i<nums.length; i++){
            if(visited[i] == 0){
                visited[i] = 1;
                if(canPartition(nums, visited, i+1, k, cur_sum + nums[i], cur_num++, target))return true;
                visited[i] = 0;
            }
        }
        return false;
    }

start_index 是用來優化時間復雜度的,避免重復考慮。運行時間為7ms

我稍微修改一下後,運行時間為4ms,代碼如下

class Solution {
    public boolean canPartitionKSubsets(int[] nums, int k) {
        int sum = 0;
        for(int num : nums) sum += num;
        if(sum % k != 0) return false;
        int tgt = sum / k;
        boolean[] visited = new boolean[nums.length];
        return dfs(nums, visited, 0, k, 0, tgt);
    }
    public boolean dfs(int[] nums, boolean[] visited, int start, int k, int sum, int tgt) {
        if(k == 1) return true;
        if(sum == tgt) return dfs(nums, visited, 0, k - 1, 0, tgt);
        for(int i = start; i < nums.length; i++) {
            if(!visited[i] && tgt - sum >= nums[i]) {
                visited[i] = true;
                if(dfs(nums, visited, i + 1, k, sum + nums[i], tgt)) return true;
                visited[i] = false;
            }
        }
        return false;
    }
}

時間復雜度應該為O(k*2^n)。對於個k個子集合,每個子集合的和為target,對於沒有被訪問過的每個數,都有兩種可能,即是否加入當前的子集合。

LeetCode 698 Partition to K Equal Sum Subsets