LC416分割等和子集
给你一个 只包含正整数 的 非空 数组 nums
。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
示例 1:
输入:nums = [1,5,11,5] 输出:true 解释:数组可以分割成 [1, 5, 5] 和 [11] 。
示例 2:
输入:nums = [1,2,3,5] 输出:false 解释:数组不能分割成两个元素和相等的子集。
class Solution {
public boolean canPartition(int[] nums) {
int sum = 0;
for(int i : nums) sum += i;
if(sum % 2 != 0) return false;
int target = sum / 2;
int yes, no;
// 01背包
int[][] dp = new int[nums.length][target];
//初始化第一行
for(int i = 0; i < target; i++) dp[0][i] = nums[0] > target ? 0 : nums[0];
//遍历剩余容量
for(int i = 1; i < nums.length; i++) {
for(int j = 0; j < target; j++) {
// 不选nums[i]
no = dp[i - 1][j];
// 选nums[i]
yes = nums[i] > j ? 0 : dp[i-1][j - nums[i]] + nums[i];
dp[i][j] = Math.max(yes, no);
if(dp[i][j] == target) return true;
}
}
return false;
}
}
空间优化,一维数组:倒序遍历是为了保证物品i只被放入一次!。但如果一旦正序遍历了,那么物品0就会被重复加入多次!
class Solution {
public boolean canPartition(int[] nums) {
int n = nums.length;
//「等和子集」的和必然是总和的一半
int sum = 0;
for (int i : nums) sum += i;
int target = sum / 2;
// 对应了总和为奇数的情况,注定不能被分为两个「等和子集」
if (target * 2 != sum) return false;
// 将「物品维度」取消
int[] f = new int[target + 1];
for (int i = 0; i < n; i++) {
int t = nums[i];
// 将「容量维度」改成从大到小遍历
for (int j = target; j >= 0; j--) {
// 不选第 i 件物品
int no = f[j];
// 选第 i 件物品
int yes = j >= t ? f[j-t] + t : 0;
f[j] = Math.max(no, yes);
}
}
// 如果最大价值等于 target,说明可以拆分成两个「等和子集」
return f[target] == target;
}
}
LC152.乘积最大子数组
给你一个整数数组 nums
,请你找出数组中乘积最大的非空连续
子数组
(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。
测试用例的答案是一个 32-位 整数。
示例 1:
输入: nums = [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6。
示例 2:
输入: nums = [-2,0,-1] 输出: 0 解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。
class Solution {
public int maxProduct(int[] nums) {
int res = Integer.MIN_VALUE;
int max = 1, min = 1;
for(int i = 0; i < nums.length; i++) {
if(nums[i] < 0) {
int temp = max;
max = min;
min = temp;
}
min = Math.min(nums[i], min * nums[i]);
max = Math.max(nums[i], max * nums[i]);
res = Math.max(res, max);
}
return res;
}
}
重点:由于存在负数,那么会导致最大的变最小的,最小的变最大的。因此还需要维护当前最小值min