算法-Partition Equal Subset Sum(动态规划)
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法-Partition Equal Subset Sum(动态规划)相关的知识,希望对你有一定的参考价值。
一直以来,动态规划是我的问题,今天看到了一道动态规划的题,做了好久,也思考了好久,借鉴别人的代码才把这个问题理解到。
题意:
Given a non-empty array containing only positive integers, find if the array can be partitioned into two subsets such that the sum of elements in both subsets is equal.
样例:
Given nums = [1, 5, 11, 5], return true two subsets: [1, 5, 5], [11] Given nums = [1, 2, 3, 9], return false
基本的意思就是说,给定一个数组,能不能将这个数组分为两个部分,同时这两个部分的和是相同的。
我刚刚看到这个题时,想到的是用回溯法来做,因为这个是回溯法的子集树。但是,超时了!
1.回溯法(超时)
1 private boolean flag = false; 2 public boolean canPartition(int[] nums) { 3 Integer target = Arrays.stream(nums).boxed().reduce(0, (a, b) -> a + b); 4 if(target % 2 != 0){ 5 return false; 6 } 7 return backTrack(nums, 0, 0, target / 2); 8 } 9 10 private static boolean backTrack(int nums[], int start, int sum , int target) { 11 if(sum == target){ 12 flag = true; 13 } 14 else 15 { 16 for(int i = start; i < nums.length; i++){ 17 if(sum + nums[i] <= target){ 18 backTrack(nums, i + 1, sum + nums[i], target); 19 } 20 } 21 } 22 return flag; 23 }
后来一想,这个跟0-1背包很像,0-1背包也是选择一些物品来转,而这里是选择一些数字,保证两个部分的和是相同的。于是照着0-1背包的方法来做。
2.类似于0-1背包的做法
1 public static boolean canPartition(int[] nums) { 2 int sum = Arrays.stream(nums).reduce(0, Integer::sum); 3 if (sum % 2 == 1) 4 return false; 5 else { 6 sum /= 2; 7 int n = nums.length; 8 // dp[i][j] 表示 如果我们取前i个数字,且背包容量为j的情况下,最多能放入多少东西 9 int dp[][] = new int[n][sum + 1]; 10 // dp[0][0] 为初始状态,表示,没有任何没有东西没有体积,其余部分初始化 11 for (int i = nums[0]; i <= sum; i++) { 12 dp[0][i] = nums[0]; 13 } 14 // 遍历n个数字,即视为n个产品 15 for (int i = 1; i < n; i++) { 16 // 加入了这种物品后更新状态 17 for (int j = nums[i]; j <= sum; j++) { 18 dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - nums[i]] + nums[i]); 19 } 20 } 21 // 放满了才能表示正好1/2 22 if (dp[n - 1][sum] == sum) 23 return true; 24 else 25 return false; 26 } 27 }
后来看了网上其他的代码,感觉另一种方法更好。
3.其他做法(动态规划)
1 public boolean canPartition(int[] nums) { 2 int sum = Arrays.stream(nums).reduce(0, Integer::sum); 3 int target = sum / 2; 4 if(sum % 2 != 0){ 5 return false; 6 } 7 boolean [] dp = new boolean[target + 1]; 8 dp[0] = true; 9 for(int i = 0; i < nums.length; i++){ 10 for (int j = target; j >= nums[i]; j--) { 11 dp[j] = dp[j] || dp[j - nums[i]]; 12 } 13 } 14 return dp[target]; 15 }
上面的代码用的是也是动态规划,但是只用了一维数组。
其中意思是:
我们定义dp数组来表示某一个数字是否是数组nums的任意一个集合的和,比如,dp[2]表示的是2是否是nums的任意一个集合的和,如果是的话,那为true,否则为false。所以我们最终看dp[sum / 2](sum为数组nums的和)是否为true。但是我们怎么得到这个dp数组呢?
首先,我们得到dp[sum / 2]就行,所以数组的长度最好是sum + 1,因为dp[0]不在题的范围内,所以dp求出1 ~ sum/2直接的所有值,于是我们可以得到一个二维表(实际上是一维表,但是这里为了演示清楚,才选择二维表)。dp数组默认全部为false,然后后面更新这个数组。如图所示
我们来看一个表:
以上是关于算法-Partition Equal Subset Sum(动态规划)的主要内容,如果未能解决你的问题,请参考以下文章
算法: 零一背包问题416. Partition Equal Subset Sum
算法: 零一背包问题416. Partition Equal Subset Sum
算法: 数组是否可以分成和相等的两个子集416. Partition Equal Subset Sum
刷题416. Partition Equal Subset Sum