【LeetCode每日一题】[中等]416. 分割等和子集
416. 分割等和子集
题目来源
算法思想:数组,子集
题目精炼:给定一个只包含正整数的非空数组 nums,判断是否可以从数组中选出一些数字,使得这些数字的和等于整个数组的元素和的一半。
题目:
题目精炼:给定一个只包含正整数的非空数组 nums,判断是否可以从数组中选出一些数字,使得这些数字的和等于整个数组的元素和的一半。
java代码–超时
思路:
- 首先计算数组总和,将一半设置成目标值target;
- 将问题转化成,nums中寻找一个子集,使其和等于目标值
- 利用二进制生成子集,判断其和是否等于目标值
运行结果:超时,生成子集太慢了
public boolean canPartition(int[] nums) {
int sum = 0;//计算总和
for (int i = 0; i < nums.length; i++) {
sum += nums[i];
}
if (sum % 2 != 0) {
//如果是奇数,直接返回错误
return false;
}
int target = sum / 2;//一个子集的和为总和的一半
sum = 0;
int numSubset = (int) Math.pow(2, nums.length);//计算子集数量
//开始利用二进制生成集合的子集
for (int i = 0; i < numSubset; i++) {
//生成2^n子集,假设n=3; 序号为{000,001,010,,,}
for (int j = 0; j < nums.length; j++) {
//一个子集利用二级制来生成
if((1<<j & i) != 0) {
//用001(1)来位移(001向右位移j位),&与当前序号i,判断序号中第j位是1,然后将对应数组元素放入集合中
sum += nums[j];//计算每一个子集的总和
}
}
if (sum == target) {
//如果等于目标值,则说明存在这样的子集,剩下的元素组成另一个集合,返回true
return true;
}
sum = 0;
}
return false;
}
java代码–动态规划
思路分析:官方题解
public class Solution {
public boolean canPartition(int[] nums) {
int n = nums.length;
int sum = 0;//计算总和
int max = 0;//统计最大值
for (int i = 0; i < n; i++) {
if (nums[i] > max) {
max = nums[i];
}
sum += nums[i];
}
int target = sum / 2;//设定目标值为总和的一半
if (sum % 2 != 0 || max > target) {
//如果总和是奇数,或者最大值大于sum的一半,则直接返回false
return false;
}
// 创建二维状态数组,行:物品索引,列:容量(包括 0)
boolean[][] dp = new boolean[n][target + 1];
for (int i = 0; i < n; i++) {
dp[i][0] = true;
}
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];
}
}