1. 程式人生 > >LeetCode-【動態規劃】-分割等和子集&劃分為k個相等的子集

LeetCode-【動態規劃】-分割等和子集&劃分為k個相等的子集

1.分割等和子集

給定一個只包含正整數非空陣列。是否可以將這個陣列分割成兩個子集,使得兩個子集的元素和相等。

注意:

  1. 每個陣列中的元素不會超過 100
  2. 陣列的大小不會超過 200

示例 1:

輸入: [1, 5, 11, 5]

輸出: true

解釋: 陣列可以分割成 [1, 5, 5] 和 [11].

示例 2:

輸入: [1, 2, 3, 5]

輸出: false

解釋: 陣列不能分割成兩個元素和相等的子集.

題解:分割等和子集,那就先求和,再平分。如果不能平分,那就直接返回false,否則,問題就變成了判斷陣列是否存在若干個元素求和正好等於陣列和的一半,那就直接遍歷所有的結果,深搜解決,可以,這種方式很容易想到。還有一種方法是將這個問題轉化為揹包問題
<揹包問題詳解傳送門>,使揹包的大小為陣列和的一半,問題轉化為是否存在陣列元素使揹包正好被裝滿,也就是最優解是陣列和的一半。

深搜解法:

class Solution {
    public boolean canPartition(int[] nums) {
        int sum=0;
        for(int n:nums)
            sum+=n;
        if(sum%2!=0)
            return false;
        return subsetSum(nums,sum>>>1);
    }
    public boolean subsetSum(int[] nums, int s) {
        boolean[] dp = new boolean[s + 1]; 
        dp[0] = true;
        for (int n : nums){
            for (int i = s; i >= n; i--){
                dp[i] =dp[i]||dp[i - n]; 
            }
            if(dp[s])
                return true; 
        }
        return dp[s];
    } 
}

揹包解法:

class Solution {
    public boolean canPartition(int[] nums) {
        int n=nums.length;
        int sum=0;
        for(int i=0;i<n;i++){
            sum+=nums[i];
        }
        int t=sum/2;
        if(sum%2!=0)
            return false;
        int[] dp=new int[t+1];
        for(int i=0;i<n;i++){
            for(int j=t;j>=nums[i];j--){
                dp[j]=Math.max(dp[j],dp[j-nums[i]]+nums[i]);
            }
        }
        if(dp[t]==t)
        	return true;
        return false;
    }
}

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

給定一個整數陣列  nums 和一個正整數 k,找出是否有可能把這個陣列分成 k 個非空子集,其總和都相等。

示例 1:

輸入: nums = [4, 3, 2, 3, 5, 2, 1], k = 4
輸出: True
說明: 有可能將其分成 4 個子集(5),(1,4),(2,3),(2,3)等於總和。

注意:

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

題解:本題與上一題解法基本相同,只不過是把平分改為多分而已。

class Solution {
    public boolean canPartitionKSubsets(int[] nums, int k) {
        int n=nums.length;
        int sum=0;
        for(int i=0;i<n;i++)
            sum+=nums[i];
        boolean[] v=new boolean[n];
        if(sum%k!=0)
            return false;
        return dfs(nums,k,sum/k,0,0,v);
    }
    public static boolean dfs(int[] nums,int k,int target,int sum,int s,boolean[] v){
        if(k==1)
            return true;
        if(target<0)
            return false;
        if(target==sum)
            return dfs(nums,k-1,target,0,0,v);
        for(int i=s;i<nums.length;i++){
            if(v[i])
                continue;
            v[i]=true;
            if(dfs(nums,k,target,sum+nums[i],i+1,v))
                return true;
            v[i]=false;
        }
        return false;
    }
}