一、题目描述
给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
(nums[i]<=100;nums.length<=200)
示例:
输入:[1, 5, 11, 5]
输出:true
即数组分割为[1, 5, 5]和 [11]
输入:[1, 2, 3, 4]
输出:false
二、思路
最重要的是转换思想,即不去考虑这个数组到底能不能分成两个和相等的数组,而是考虑可不可以从这个数组中取一些元素,使得它们的和等于数组总和的一半。
转换思路后,可以直接使用动态规划解题。
确定状态:f[i][j]表示从0-i这些元素中是否可以组成j
转移方程:f[i][j] = f[i-1][j] || f[i-1][j-nums[j]],即两种情况,第一种为前i-1个元素即可组成 j,第二种情况为只有加入 i 后,才能组成 j。
边界条件:f[i][0] = true f[0][nums[0]] = true
三、代码
class Solution {
public boolean canPartition(int[] nums) {
//转换思路----数组中是否可以选择一部分元素,使得其和等于数组总的和的一半
if(nums.length <2){
return false;//只有一个元素
}
int length = nums.length;
int sum = 0;
int max = 0;
//总和和最大元素值
for(int i = 0;i<length;i++){
sum += nums[i];
max = Math.max(max,nums[i]);
}
//如果总和为奇数,则不可能对半分
if(sum %2 != 0){
return false;
}
int target = sum/2;
if(max > target){
//最大元素大于总和的一半
return false;
}
//确定状态---d[i][j]表示从前i个数字中,是否能组成j
//转移方程---d[i][j] = d[i-1][j] || d[i - 1][j - nums[j]]
//d[i-1][j]为不选取nums[i]的情况,即前i-1个元素也可以组成j
//d[i-1][j - nums[i]]则是选取nums[i]的情况,即前i-1个元素组成j-nums[i],加上nums[i]后组成j
//边界条件---d[i][0] = true d[0][nums[0]] = true
boolean[][] f = new boolean[length][target + 1];
f[0][nums[0]] = true;
f[0][0] = true;
for(int i =1;i<length;i++){
for(int j = 0;j<=target;j++){
if(j==0){
f[i][0] = true;
}else{
if(j>=nums[i]){
f[i][j] = f[i-1][j] || f[i - 1][j - nums[i]];
}else{
//当j < nums[i]时,只能看前i-1个数是否能凑出j来
f[i][j] = f[i-1][j];
}
}
}
}
return f[length-1][target];
}
}