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.
Seen this question in a real interview before?
把一個數組分成兩組,看和是否相等。注意這不是連續數組。比如[3 4 6 5] 不能用區間和計算
思路:1個數選或不選。
首先求得總和,要求選其中的數的和是總和的一半。
建立矩陣dp[i][j],表示j這個和能否由前i個數組成。
邊界條件:
(1) dp[0][0]=true
(2)dp[i][0]=true,對于所有的i隻要一個數都不選,就可以了
(3)dp[0][j]=false 0個數不能組成1一上的數,隻能組成0
傳遞條件:
dp[i][j]=dp[i-1][j]||dp[i-1][j-nums[i]] 說明,如果前i-1個數能組成j,那麼前i個數一定能組成j,隻要不加nums[i];如果前i-1個數能組成j-nums[i],那麼前i個數能組成j,隻要将nums[i]加上即可。這兩種情況由一種滿足,目前值就是true。
class Solution {
public:
bool canPartition(vector<int>& nums) {
int n=nums.size();
if(n<=1) return false;
int sum=accumulate(nums.begin(),nums.end(),0);
if(sum&1) return false;
sum/=2;
vector<vector<bool>> dp(n+1,vector<bool>(sum+1,false));//這裡的i,從1位置表示幾個數,sum也是從1開始計算,因為0個數也要考慮,sum是求和的值,也包括0這個意義
dp[0][0]=true;
for(int i=1;i<=n;i++)
dp[i][0]=true;
//初始化dp[0][j]已經是false
for(int i=1;i<=n;i++)
for(int j=1;j<=sum;j++)
{
dp[i][j]=dp[i-1][j];//
if(j>=nums[i-1])//有等号,和可以是0
{
dp[i][j]=dp[i-1][j]||dp[i-1][j-nums[i-1]];//注意nums[i-1]是從0開始記得
}
}
return dp[n][sum];
}
};
空間優化:
發現橫坐标至于前面一行有關,而且j的周遊也是從小到大的。是以j應該從大到小更新,否則會覆寫舊知
class Solution {
public:
bool canPartition(vector<int>& nums) {
int n=nums.size();
if(n<=1) return false;
int sum=accumulate(nums.begin(),nums.end(),0);
if(sum&1) return false;
sum/=2;
vector<bool> dp(sum+1,false);//這裡的i,從1位置表示幾個數,sum也是從1開始計算,因為0個數也要考慮,sum是求和的值,也包括0這個意義
dp[0]=true;
//初始化dp[0][j]已經是false
for(int i=1;i<=n;i++)
for(int j=sum;j>=1;j--)
{
if(j>=nums[i-1])//有等号,和可以是0
{
dp[j]=dp[j]||dp[j-nums[i-1]];//注意nums[i-1]是從0開始記得
}
}
return dp[sum];
}
};