1.题目描述
给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
注意:
每个数组中的元素不会超过 100
数组的大小不会超过 200
示例 1:
输入: [1, 5, 11, 5]
输出: true
解释: 数组可以分割成 [1, 5, 5] 和 [11].
示例 2:
输入: [1, 2, 3, 5]
输出: false
解释: 数组不能分割成两个元素和相等的子集.
2.解题思路
使用方法:动态规划
1).判断数组长度是否小于2,否则返回false.(根据题目是否可以将这个数组分割成两个子集)
2).计算整个数组的和,判断和是偶数还是奇数,如果是奇数则说明不能划分为两个子集,返回false.如果是偶数则继续往下进行。
3).寻找整个数组中的最大值,判断最大值是否等于和(整个数组的和)的一半,如果大于需返回false.
4).接下来将使用动态规划的方法,将问题转为0-1背包问题来处理。
(target+1为背包的最大重量)对二维数据dp进行遍历,数组的默认结果为false。
1.边界问题:
a.)dp[i][0]无论如何遍历 ,都有dp[i][0]==true
b.)dp[0][nums[0]]==true,无论nums[0]如何变化,nums[0]都是它本身的重量值。
2.当j>=nums[i]时,有dp[i][j]=dp[i-1][j] | dp[i-1][j-nums[i]];
当j<nums[i]时,就有dp[i][j]=dp[i-1][j];
3.Java代码具体实现
class Solution {
public boolean canPartition(int[] nums) {
int n = nums.length;
// 是否可以将这个数组分割成两个子集
if(n <2){
return false;
}
int sum=0,maxNum=0;
for(int num:nums){
sum+=num;
maxNum=Math.max(maxNum,num);
}
if(sum %2 !=0){
return false;
}
//target为数组和的一半
int target = sum/2;
if(maxNum > target){
return false;
}
boolean[][] dp = new boolean[n][target+1];
//dp[i][0]表示重量为0 则都为true
for(int i=0;i<n;i++){
dp[i][0]=true;
}
//nums[0]其实也是第一个数的重量
dp[0][nums[0]] = true;
for(int i=1;i<n;i++){
int num=nums[i];
for(int j=1;j<=target;j++){
if(j>=num){
dp[i][j] = dp[i-1][j] | dp[i-1][j-num];
}else{
dp[i][j] = dp[i-1][j];
}
}
}
return dp[n-1][target];
}
}
之前有个问题不理解为什么dp[0][nums[0]=true.nums[0]为数据的最大重量,而当你处于dp[0][nums[0]重量是相等的,所以肯定为true。
tips:什么是动态规划,如何理解动态规划的0-1背包问题?