【LeetCode】416. Partition Equal Subset Sum 解題報告(Python & C++)
版權宣告:本文為博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/fuxuemingzhu/article/details/79787425
作者: 負雪明燭
id: fuxuemingzhu
個人部落格: http://fuxuemingzhu.cn/
目錄
題目描述
題目大意
解題方法
DFS
動態規劃
日期
題目地址:https://leetcode.com/problems/partition-equal-subset-sum/description/
題目描述
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:
Each of the array element will not exceed 100.
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.
題目大意
判斷是否可以把一組數字分成兩堆,兩堆數字的和相等。
解題方法
DFS
首先要判斷所有數字的和是不是偶數,然後我們使用一個長度為2的陣列進行儲存我們要平分得到的target,這麼做是我們可以通過使用-,+兩種操作來跳過一些數字。同樣是dfs,這裡的dfs操作允許跳過某些位置去向下尋找,只要找到一個滿足條件的就可以停止。而subsets的題不可以這麼做,因為我們要找到所有的可能的答案。因此可以看做是兩套模板。
Python程式碼:
class Solution:
def canPartition(self, nums):
"""
:type nums: List[int]
:rtype: bool
"""
_sum = sum(nums)
div, mod = divmod(_sum, 2)
if mod or max(nums) > div: return False
nums.sort(reverse = True)
target = [div] * 2
return self.dfs(nums, 0, target)
def dfs(self, nums, index, target):
for i in range(2):
if target[i] >= nums[index]:
target[i] -= nums[index]
if target[i] == 0 or self.dfs(nums, index + 1, target): return True
target[i] += nums[index]
return False
動態規劃
這個題其實是個0-1揹包問題。所以可以使用動態規劃求解。
求和是必須的,目標target等於和的一半。如果和不是偶數的話則一定不可能由陣列構成出來,直接返回false.
首先定義dp陣列為dp[i][j],其意義是使用前i個數字的和能不能構成整數j。我們需要把每個位置都進行遍歷,同時也要對0~target之間的所有正整數進行遍歷。很顯然,狀態轉移的方程是,遍歷到i位置時能不能構成target = 前面的數字的和能夠成target || 前面的數字能構成target - nums[i]。這兩個狀態分別是選不選取nums[i]的兩種情況,如果有一種情況成立即可。
狀態轉移方程如下:
dp[i][j] = dp[i - 1][j] || dp[i - 1][j - nums[i]]
1
這個題的技巧和難點就在於,需要把陣列的每個數字的和當做dp的一個狀態,這個是很少見的,其實題目給的有提示:陣列的每個數都是整數,並且數字不會超過200,陣列長度不超過100,這些說明了數字的和不會太大不會太多。
具體C++程式碼如下:
class Solution {
public:
bool canPartition(vector<int>& nums) {
int sum = accumulate(nums.begin(), nums.end(), 0);
const int N = nums.size();
int target = sum >> 1;
if (sum % 2 != 0) return false;
//dp[i][j] means whether we can sum to j using first i numbers.
vector<vector<bool>> dp(N + 1, vector<bool>(sum + 1, false));
// every number can build number 0.
for (int i = 0; i <= N; ++i) {
dp[i][0] = true;
}
// but for position 0, it can build number nothing.
for (int j = 1; j <= target; ++j) {
dp[0][j] = false;
}
// anyway, position 0 can build number 0.
dp[0][0] = true;
for (int i = 1; i <= N; ++i) {
for (int j = 0; j <= target; ++j) {
if (j >= nums[i - 1])
dp[i][j] = dp[i - 1][j] || dp[i - 1][j - nums[i - 1]];
else
dp[i][j] = dp[i - 1][j];
}
}
return dp[N][target];
}
};
從上面的程式碼中可以看出,每個位置的狀態只與其前面位置的狀態有關,所以可以做狀態壓縮節約空間複雜度。
只使用一維dp陣列,dp[j]表示從陣列中任意取數字的和能不能構成j。狀態轉移方程就是忽略掉二維陣列的第一個維度即可,即:
dp[j] = dp[j] || dp[j - nums[i]]
1
還要說一下,為什麼需要從後向前更新dp,這是因為每個位置依賴與前面的一個位置加上nums[i],如果我們從前向後更新的話,那麼dp[i - 2]會影響dp[i - 1],然後dp[i - 1]接著影響dp[i],即同樣的一個nums[i]被反覆使用了多次,結果肯定是不正確的。但是從後向前更新就沒有問題了。
那麼結合上面的分析,可以寫出如下程式碼:
class Solution {
public:
bool canPartition(vector<int>& nums) {
int sum = accumulate(nums.begin(), nums.end(), 0);
const int N = nums.size();
if (sum % 2 != 0) return false;
int target = sum >> 1;
vector<bool> dp(sum + 1, false);
dp[0] = true;
for (int num : nums) {
for (int j = target; j >= num; --j) {
dp[j] = dp[j] || dp[j - num];
}
}
return dp[target];
}
};
日期
2018 年 4 月 2 日 —— 要開始準備ACM了
2019 年 1 月 8 日 —— 別熬夜,我都開始有黑眼圈了。。
---------------------
作者:負雪明燭
來源:CSDN
原文:https://blog.csdn.net/fuxuemingzhu/article/details/79787425
版權宣告:本文為博主原創文章,轉載請附上博文連結!
相關文章
- [LeetCode] 416. Partition Equal Subset SumLeetCode
- 【LeetCode】253. Meeting Rooms II 解題報告(C++)LeetCodeOOMC++
- leetcode 831題解【C++/Java/Python】LeetCodeC++JavaPython
- [題解]AT_abc321_f [ABC321F] #(subset sum = K) with Add and Erase
- python: leetcode - 1 Two SumPythonLeetCode
- CF1270G Subset with Zero Sum
- Leetcode Weekly Contest 95解題報告LeetCode
- LeetCode Weekly Contest 96 解題報告LeetCode
- 【每日一題-leetcode】416. 分割等和子集每日一題LeetCode
- AtCoder ABC321F - #(subset sum = K) with Add and Erase 題解 可撤銷揹包
- ABC 321 F #(subset sum = K) with Add and Erase
- Leetcode Weekly Contest94 解題報告LeetCode
- LeetCode刷題日記 416. 分割等和子集LeetCode
- Leetcode 第136場周賽解題報告LeetCode
- LeetCode #1:Two Sum(簡單題)LeetCode
- python leetcode 之兩數之和(two sum)PythonLeetCode
- leetcode 368. Largest Divisible SubsetLeetCode
- Leetcode Path SumLeetCode
- leetcode Sum系列LeetCode
- LeetCode C++ 1302. Deepest Leaves Sum【Tree/BFS/DFS】中等LeetCodeC++
- LeetCode刷題記錄與題解(C++版本)LeetCodeC++
- LeetCode | 1 Two SumLeetCode
- Leetcode 39 Combination SumLeetCode
- Leetcode 1 two sumLeetCode
- [LeetCode] 2831. Find the Longest Equal SubarrayLeetCode
- 18. 4Sum(Leetcode每日一題-2020.10.05)LeetCode每日一題
- [LeetCode] 2491. Divide Players Into Teams of Equal SkillLeetCodeIDE
- 【Leetcode】453. Minimum Moves to Equal Array ElementsLeetCode
- LeetCode-1 Two SumLeetCode
- LeetCode 112. Path SumLeetCode
- leetcode-39-Combination SumLeetCode
- Leetcode 40 Combination Sum IILeetCode
- Leetcode 15 3SumLeetCode
- Leetcode 18 4SumLeetCode
- [LeetCode]1.Two SumLeetCode
- LeetCode題解(0407):接雨水II(Python)LeetCodePython
- LeetCode題解(1652):拆炸彈(Python)LeetCodePython
- LeetCode題解(1178):猜字謎(Python)LeetCodePython