日期:2020-10-11
作者:19届 LZ
标签:JAVA NP完全问题
题目描述
给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
注意:
每个数组中的元素不会超过 100
数组的大小不会超过 200
示例:
示例 1:
输入: [1, 5, 11, 5]
输出: true
解释: 数组可以分割成 [1, 5, 5] 和 [11].
示例 2:
输入: [1, 2, 3, 5]
输出: false
解释: 数组不能分割成两个元素和相等的子集.
解题思路:
存在几种情况可以直接舍弃:
1,数组大小为1或0;
2,数组和为奇数
3,数组存在某个数大于数组和的一半
设立一个数组dp[i][j],表示是否可以从nunms[0]到nums[i]中选取元素使其和为j;
则以下条件恒成立
dp[i][0]=true;
dp[0][nums[0]]=true;
状态转移方程:
当i>0,j>0时:
如果nums[i]<=j,则dp[i][j]可以由dp[i-1][j-nums[i]]或者dp[i-1][j]得到
如果nums[i]>j,则dp[i][j]只能由dp[i-1][j]得到
代码
class Solution {
public boolean canPartition(int[] nums) {
int n=nums.length;
if(n<2){//舍弃数组大小为1或0的情况
return false;
}
int all=0,max=0;
for(int num:nums){
all+=num;
max=Math.max(num,max);
}
if(all%2==1){//和为奇数,舍去
return false;
}
int target=all/2;
if(max>target)//舍弃存在一个数大于和的一半的情况
return false;
boolean[][] dp = new boolean[n][target + 1];
//dp[i][j]表示是否可以从nunms[0]到nums[i]中选取元素和为j
for(int i=0;i<n;i++){
dp[i][0]=true;//若不选取,和为0恒成立
}
dp[0][nums[0]]=true;//只能选择nums[0],所以恒成立
for(int i=1;i<n;i++){ //状态转移方程
for(int j=1;j<target+1;j++){
if(nums[i]<=j){
dp[i][j]=dp[i-1][j-nums[i]]|dp[i-1][j];
}
else{
dp[i][j]=dp[i-1][j];
}
}
}
return dp[n-1][target];
}
}