LeetCode 698 Partition to K Equal Sum Subsets
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