416. 分割等和子集(记忆化搜索、DP动态规划) - 力扣(LeetCode)
分割等和子集
给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
示例 1:
输入:nums = [1,5,11,5]
输出:true
解释:数组可以分割成 [1, 5, 5] 和 [11] 。
示例 2:
输入:nums = [1,2,3,5]
输出:false
解释:数组不能分割成两个元素和相等的子集。
题解
记忆化搜索
dp的思路,但是使用的是记忆化搜索
一维是数据下标,二维代表的是到此下标时累加到的和
我们用二维数组来记录从此时的状态出发 是否 可以累加到target(目标值),下次计算到这个状态时,直接拿取结果就行。
这样避免了重复计算
class Solution { public boolean canPartition(int[] nums) { int sum = 0; for(int t : nums) sum += t; if(sum%2==1) return false; mem = new int [nums.length+1][sum/2+1]; for(int i=0; i<nums.length; ++i) Arrays.fill(mem[i], -1); return dfs(0, 0, sum/2, nums); } int [][]mem; boolean dfs(int idx, int sum,int target,int []nums){ if(sum>=target) return sum == target; if(idx>=nums.length) return false; if(mem[idx][sum]!=-1) return mem[idx][sum] == 1; boolean res = dfs(idx+1, sum+nums[idx], target,nums) || dfs(idx+1,sum,target,nums); mem[idx][sum] = res == true ? 1 : 0; return res; }}
动态规划
class Solution { /* dp[i][j]表示从前i个元素中选择和不超过j的方案 属性:是否存在 按选不选当前元素划分 dp[i][j] = dp[i-1][j] || dp[i][j-u] */ public boolean canPartition(int[] nums) { int n = nums.length; int sum = 0; for(int i=0; i<n; ++i) sum+=nums[i]; if(sum%2==1) return false; sum = sum / 2; boolean dp[][] = new boolean[n+1][sum+1]; for(int i=0; i<=n; ++i) dp[i][0] = true; for(int i=1; i<=n; ++i){ for(int j=1; j<=sum; ++j){ if(j>=nums[i-1]){ dp[i][j] = dp[i-1][j] || dp[i-1][j-nums[i-1]]; } else dp[i][j] = dp[i-1][j]; } } return dp[n][sum]; }}