leetcode-zgd-day42-01背包康复训练/416.分割等和子集

105 阅读2分钟

416.分割等和子集

题目链接:416. 分割等和子集 - 力扣(LeetCode)

解题思路:

首先使用背包的思想去模拟这道题,想清楚背包的容量以及要计算的最大价值是什么,这个题目可以理解为物品的价值和物品的重量是相等的。想要分割出相等的两个子集,那么只需要找出那么一组数字,能够将所有数字的和sum/2大小的背包正好装满,即可证明能够将数组分割成相等的两部分。

 class Solution {
     public boolean canPartition(int[] nums) {
         int sum = 0;
         for(int i : nums){
             sum += i;
         }
         if(sum % 2 == 1) return false;
         sum = sum / 2;
         /**
          * 1.确定dp数组及其下标含义 dp[i][j] 从下标0-i数字当中选取的和不超过j的最大和
          * 2.dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - nums[i]] + nums[i])
          * 3.dp[i][0] = 0  dp[0][j]  if(j < nums[0]) = 0  else = nums[0]
          * 正向循环
          */
         int[][] dp = new int[nums.length + 1][sum + 1];
         for(int i = 0; i < nums.length; i++){
             dp[i][0] = 0;
         }
         for(int i = 0; i <= sum; i++){
             if(i > nums[0]){
                 dp[0][i] = nums[0];
             }
         }
         for(int i = 1; i < nums.length; i++){ // 外层循环物品
             for(int j = 1; j <= sum; j++){
                 if(j >= nums[i]){
                     dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - nums[i]] + nums[i]);
                 }
                 else dp[i][j] = dp[i - 1][j];
             }
         }
         if(dp[nums.length - 1][sum] == sum) return true;
         return false;
     }
 }

思考:

当使用二维数组进行递推的时候,有两个问题并不需要考虑,一个是循环的内外层顺序,一个是循环的方向。因为前面已经确定下来的值,并不会影响后续的值。

但是当使用一维数组进行递推的时候,情况就不一样了。内层的背包容量循环需要考虑到循环的方向问题,是正向循环还是反向循环,答案是反向循环,因为当前的递推会使用到前面的以前二维的上一层的值,如果正向循环,前面的值还没等后面的同一层的值使用,就已经被更新为新一层的内容了,这肯定是不行的,所以要从后向前循环。

同时对代码中for循环内嵌套if的语句进行了调整优化。

代码优化:

 class Solution {
     public boolean canPartition(int[] nums) {
         int sum = 0;
         for(int i : nums){
             sum += i;
         }
         if(sum % 2 == 1) return false;
         sum = sum / 2;
         /**
          * 1.确定dp数组及其下标含义 dp[j] 背包容量为j时,背包能够装的最大重量
          * 2.dp[j] = Math.max(dp[j], dp[j - nums[i]] + nums[i])
          * 3.dp[0] = 0  dp[j]  if(j < nums[0]) = 0  else = nums[0]
          * 正向循环
          */
         int[] dp = new int[sum + 1];
         for(int j = nums[0]; j <= sum; j++){
             dp[j] = nums[0];
         }
         for(int i = 1; i < nums.length; i++){ // 外层循环物品
             for(int j = sum; j >= nums[i]; j--){
                 dp[j] = Math.max(dp[j], dp[j - nums[i]] + nums[i]);
             }
         }
         if(dp[sum] == sum) return true;
         return false;
     }
 }