[LeetCode] 416. Partition Equal Subset Sum 解題報告

Given a non-empty array containing only positive integers, find if the array can be partitioned into two subsets such that the sum of elements in both subsets is equal.

Note:

  1. Each of the array element will not exceed 100.
  2. The array size will not exceed 200.

Example 1:

Input: [1, 5, 11, 5]

Output: true

Explanation: The array can be partitioned as [1, 5, 5] and [11].

Example 2:

Input: [1, 2, 3, 5]

Output: false

Explanation: The array cannot be partitioned into equal sum subsets.

這個問題看起來還是有一些難度的,實際上是一個01揹包問題,通過動態規劃來解決。把數字看成是物品,數字的值看成是價值,重量不限,目標也不太一樣,需要確定是否能夠達到價值加起來是否能達到總和的一半。這個思路想起來有點複雜,我們換一種思考方式:

源代碼來自:https://discuss.leetcode.com/topic/62312/java-solution-similar-to-backpack-problem-easy-to-understand

我們先有一個boolean型數組dp[],用來存儲對應的數值能不能通過加這些數字得到,數組的大小就是總和的一半(超過一半的不需要判斷),記作volume,然後從小到大來判斷是否能達到,最終返回dp[volume]的值即可,如果爲true,則說明能夠通過數字的加和得到綜合的一半。

		boolean[] dp = new boolean[volumn + 1];

接下來,我們就通過兩種循環來依次判斷基於前i個數,能湊成的總和是哪些,如果可以湊成,那麼將dp對應的位置設置爲true即可。具體的方法就和揹包問題非常相似了。
dp[j] = dp[j] || dp[j - nums[i - 1]];
上面這句話可以解讀爲:對於前i個數字,能湊成總和爲j的,有兩種情況:1.如果dp數組中已經記錄了可以湊成j,那肯定沒問題;2.如果當前的大小j,減去當前這個數字,還能夠湊成的話,那麼加上當前數字,肯定也可以湊成,所以這時候也能湊成j。

這時,弱雞的我想到了一個問題,如果遇到重複的數字怎麼辦?其實並沒有關係,因爲這針對的前i個數字,有重複的繼續算就可以了。下面代碼,我作了一點點修改,就是先對nums進行排序,排序的複雜度是nlogn,加上以後,對於數量級並沒有影響。但是在循環裏面,會大量出現dp[j]=true的情況,這樣可以減少後面的比較次數。

public class Solution {
	public boolean canPartition(int[] nums) {
		if (nums == null || nums.length == 0) {
			return true;
		}
		int volumn = 0;
		Arrays.sort(nums);
		for (int num : nums) {
			volumn += num;
		}
		if (volumn % 2 != 0) {
			return false;
		}
		volumn /= 2;
		boolean[] dp = new boolean[volumn + 1];
		dp[0] = true;
		for (int i = 1; i <= nums.length; i++) {
			for (int j = volumn; j >= nums[i - 1]; j--) {
				dp[j] = dp[j] || dp[j - nums[i - 1]];
			}
		}
		return dp[volumn];
	}
}


發佈了55 篇原創文章 · 獲贊 12 · 訪問量 21萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章