416. 分割等和子集
题目
给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
注意:
每个数组中的元素不会超过 100
数组的大小不会超过 200
示例 1:
输入: [1, 5, 11, 5]
输出: true
解释: 数组可以分割成 [1, 5, 5] 和 [11].
思路
可以计算数组的和,然后除以2,判断数组中是否可以找到一些数,正好相加等于和的一半
定义数据f[N][T]表示状态,其中f[i][j]表示前i数是否可以找出一些数据相加等于j,f是一个bool数组。
状态转移方程为
f
[
i
]
[
j
]
=
f
[
i
−
1
]
[
j
]
∣
∣
f
[
i
−
1
]
[
j
−
A
[
i
]
]
f[i][j] = f[i-1][j] || f[i-1][j-A[i]]
f[i][j]=f[i−1][j]∣∣f[i−1][j−A[i]],
∣
∣
||
∣∣表示逻辑或,具体实现看代码
代码
public boolean canPartition(int[] nums) {
int sum = 0;
for (int i:nums){
sum += i;
}
if(sum %2==1){
return false;
}
int target = sum/2;
int n = nums.length;
//定义状态数组,每个维度多定义了一个
boolean f[][] = new boolean[n+1][target+1];
f[0][0] = true; //定义初始状态,表示0个数据可以相加得到0
//这个在DP中很重要,只是不同的题目初始状态不同
//有的问题,定义一个F[N+1][M+1]的数组,F[0][0]或者F[0][j]默认为0,直接可作为初始状态,
//不需要额外改变,但是这个问题不行。初始状态的定义非常重要
for (int i=1;i<=n;i++){
for (int j=0;j<=target;j++){
if(nums[i-1]<=j){
f[i][j] = f[i-1][j-nums[i-1]] || f[i-1][j];
}else {
f[i][j] = f[i-1][j];
}
}
}
return f[n][target];
}