- 分割等和子集
给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
示例 1:
输入:nums = [1,5,11,5]
输出:true
解释:数组可以分割成 [1, 5, 5] 和 [11] 。
示例 2:
输入:nums = [1,2,3,5]
输出:false
解释:数组不能分割成两个元素和相等的子集。
本来想过双指针,但是指针夹住的和是固定的,数的位置也是固定的,也就是说只能算出连续挨着的数的和,不能实现自由组合。所以不行
答案:和01背包问题思路类似。01背包问题
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 n= nums.length;
//dp[i][j]表示前i个数,总和不超过j的最大值
int dp[][]=new int[n+1][target+1];//因为包含了第0个数,所以数组要多开一位
dp[0][target]=0;
for(int i =1;i<=n;i++){
int t=nums[i-1];//表示第i个数,不是nums[0]表示第一个数。
for(int j=0;j<=target;j++){
if (j >= t) {
//可以选当前数
dp[i][j]=Math.max(dp[i-1][j],dp[i-1][j-t]+t);
}else{
//不选
dp[i][j]=dp[i-1][j];
}
}
}
return dp[n][target] == target;
}
}
法二:
对于dp的定义有所改变。
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 n= nums.length;
//dp[i][j]表示前i个数,总和是否能凑出恰好为j的情况
boolean dp[][]=new boolean[n+1][target+1];//因为包含了第0个数,所以数组要多开一位
dp[0][0]=true;
for(int i =1;i<=n;i++){
int t=nums[i-1];//表示第i个数,不是nums[0]表示第一个数。
for(int j=0;j<=target;j++){
if (j>= t) {
//可以选当前数
dp[i][j]= dp[i-1][j] || dp[i-1][j-t];
}else{
//不选
dp[i][j]=dp[i-1][j];
}
}
}
return dp[n][target];
}
}
自己写了一个带输入的:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Main{
public static void main(String[] args) throws IOException {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String string[]= in.readLine().split(" ");
int N= Integer.parseInt(string[0]);
int[] nums = new int[N];
String string1[] = in.readLine().split(" ");
for(int i=0;i<N;i++){
nums[i]=Integer.parseInt(string1[i]);
}
System.out.println(new Main().canPartition(nums));
}
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 n= nums.length;
//dp[i][j]表示前i个数,总和不超过j的最大值
int dp[][]=new int[n+1][target+1];
dp[0][target]=0;
for(int i =1;i<=n;i++){
int t=nums[i-1];//表示第i个数,不是nums[0]表示第一个数。
for(int j=0;j<=target;j++){
if (j >= t) {
//可以选当前数
dp[i][j]=Math.max(dp[i-1][j],dp[i-1][j-t]+t);
}else{
//不选
dp[i][j]=dp[i-1][j];
}
}
}
return dp[n][target] == target;
}
}