1. 程式人生 > 其它 >698.劃分為k個相等的子集

698.劃分為k個相等的子集

回溯

預處理:
1.計算累加和s,如果s%k!=0,返回false。
2.按從小到大給陣列排序。
準備k個桶,每個桶的容量為s/k。遍歷陣列,嘗試將當前元素新增到每個桶中,如果所有所有桶都放不下則返回false。如果能遍歷完陣列則返回true。
陣列排序是一種優化操作,如果當前元素都沒有桶可以放得下,那後面的元素就不用再遍歷可以直接返回false了,因為後面的元素更大。

   public boolean canPartitionKSubsets(int[] nums, int k) {
        //因為題目限制條件不用擔心溢位
        int sum = 0;
        for(int i = 0; i < nums.length; i++){
            sum += nums[i];
        }
        if(sum % k != 0){
            return false;
        }
        //求出子集的和
        sum = sum / k;
        //排序 小的放最前面大的放最後面
        Arrays.sort(nums);
        //如果子集的和小於陣列最大的直接返回false
        if(nums[nums.length - 1] > sum){
            return false;
        }
        //建立一個長度為k的桶
        int[] arr = new int[k];
        //桶的每一個值都是子集的和
        Arrays.fill(arr, sum);
        //從陣列最後一個數開始進行遞迴
        return help(nums, nums.length - 1, arr, k);
    }
    
    boolean help(int[] nums, int cur, int[] arr, int k){
        //已經遍歷到了-1說明前面的所有數都正好可以放入桶裡,那所有桶的值此時都為0,說明找到了結果,返回true
        if(cur < 0){
            return true;
        }
        //遍歷k個桶
        for(int i = 0; i < k; i++){
            //如果正好能放下當前的數或者放下當前的數後,還有機會繼續放前面的數(剪枝)
            if(arr[i] == nums[cur] || (cur > 0 && arr[i] - nums[cur] >= nums[0])){
                //放當前的數到桶i裡
                arr[i] -= nums[cur];
                //開始放下一個數
                if(help(nums, cur - 1, arr, k)){
                    return true;
                }
                //這個數不該放在桶i中
                //從桶中拿回當前的數
                arr[i] += nums[cur];
            }
        }
        return false;
    }