[C++ code] Split equal sum subsets, target sum, one and zero, change exchange, dynamic programming--Code Thoughts

Title:Separation of equal-sum subsets

  • Give you acontaining only positive integersnon-empty Arraynums. Please determine whether this array can be divided into two subsets so that the sum of the elements of the two subsets is equal.

  • The initial idea was to use double pointers after sorting, but found that it didn't work.

    • class Solution {
              
              
      public:
          bool canPartition(vector<int>& nums) {
              
              
              if(nums.size()<2){
              
              
                  return false;
              }
              sort(nums.begin(),nums.end());
              int left=0,right=nums.size()-1;
              int left_sum=nums[left],right_sum=nums[right];
              while(left<right-1){
              
              
                  if(left_sum>right_sum){
              
              
                      right--;
                      right_sum += nums[right];
                  }else{
              
              
                      left++;
                      left_sum += nums[left];
                  }
              }
              cout<<left_sum<<" "<<right_sum<<endl;
              return left_sum==right_sum;
          }
      };//nums = [2,2,1,1]
      
  • As long as you find the sum of subsets where sum / 2 can appear in the set, it can be divided into two identical elements and subsets. You can use backtracking to violently search for all the answers, but in the end it times out, and you don’t want to optimize any more, so give up on backtracking and just go to the 01 backpack.

    • The knapsack problem has N items and a knapsack that can carry a weight of up to W. The weight of the i-th item is weight[i], and the obtained value is value[i]. Each item can only be used once. Find out which items should be put into the backpack with the highest total value. There are many backpacking methods for backpacking problems, the common ones are: 01 backpack, complete backpack, multiple backpack, group backpack, mixed backpack, etc.
  • Determine the meaning of the dp array and the subscript: dp[j] means: A backpack with a capacity of j can carry items with a maximum value of dp[j].

  • Determine the recursion formula: 01 The recursion formula of the backpack is: dp[j] = max(dp[j], dp[j - weight[i]] + value[i]); which is equivalent to putting a value in the backpack, then The weight of item i is nums[i], and its value is also nums[i]. So the recursion formula: dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]);

  • How to initialize the dp array: First, dp[0] must be 0.

  • class Solution {
          
          
    public:
        bool canPartition(vector<int>& nums) {
          
          
            // int sum=0;
            // vector<int> dp(10001,0);
            // for(auto i:nums){
          
          
            //     sum+=i;
            // }
            // if(sum % 2 == 1){
          
          
            //     return false;
            // }
            // int half_sum = sum/2;
            // for(int i=0;i<nums.size();i++){
          
          
            //     for(int j=half_sum;j>=nums[i];j--){
          
          
            //         dp[j]=max(dp[j],dp[j-nums[i]]+nums[i]);
            //     }
            // }
            // if(dp[half_sum]==half_sum){
          
          
            //     return true;
            // }
            // return false;
    };
    
  • It’s not easy to understand, but you can use a two-dimensional array, the abscissa is sum/2 for exploration, and the ordinate is element exploration.

    • class Solution {
              
              
      public:
          bool canPartition(vector<int>& nums) {
              
              
              if(nums.size()<2){
              
              
                  return false;
              }
              int sum=0;
              for(int i:nums){
              
              
                  sum+=i;
              }
              if(sum%2==1){
              
              
                  return false;
              }
              int target = sum/2;
              vector<vector<bool>> dp(nums.size(),vector<bool>(target+1,false));
              if(nums[0]<=target){
              
              
                  dp[0][nums[0]] = true;
              }
              for(int i=1;i<nums.size();i++){
              
              
                  for(int j=0;j<dp[0].size();j++){
              
              
                      dp[i][j] = dp[i-1][j];
                      if(nums[i]==j){
              
              
                          dp[i][j]=true;
                          continue;
                      }
                      if(nums[i]<j){
              
              
                          dp[i][j] = dp[i-1][j] || dp[i-1][j-nums[i]];
                      }
                  }
                  if(dp[i][target]==true){
              
              
                      return true;
                  }
              }
              return false;
          }
      };
      

Topic:The weight of the last stone II

  • There is a pile of stones, represented by an integer array stones. Where stones[i] represents the weight of the i stone. Each round, selectany two stones and smash them together. Suppose the weights of the stones are x and y, respectively, and x <= y. Then the possible results of crushing are as follows: if x == y, then both stones will be completely crushed; if x != y, then the stone with weight x will be completely crushed, and the stone with weight y will have a new weight of y-x. In the end, only one stone will be left at most. Returns the smallest possible weight of this stone. If there are no stones left, return 0.

  • The weight of the item in this question is stones[i], and the value of the item is also stones[i]. Corresponds to the weight[i] and value[i] of the items in the 01 backpack.

    • Determine the meaning of the dp array and the subscript:dp[j] represents a backpack with a capacity of j (the capacity here is more vivid, in fact it is the weight). The maximum weight that can be carried is dp[j].

    • Determine the recursion formula: 01 The recursion formula of the backpack is: dp[j] = max(dp[j], dp[j - weight[i]] + value[i]); this question is: dp[j] = max(dp[j], dp[j - stones[i]] + stones[i]);

    • How to initialize the dp array: Because the weight will not be a negative number, it is enough to initialize dp[j] to 0, so in the recursive formula dp[j] = max(dp[j], dp[j - stones[i] ] + stones[i]); dp[j] will not be overwritten by the initial value.

    • Determine the order of traversal: If you use a one-dimensional dp array, the for loop for item traversal is placed on the outer layer, the for loop for traversing the backpack is placed on the inner layer, and the inner for loop is traversed in reverse order!

    • Insert image description here

    • Then divide it into two piles of stones. The total weight of one pile of stones is dp[target], and the total weight of the other pile is sum - dp[target]. When calculating target, target = sum / 2 because it is rounded down, so sum - dp[target] must be greater than or equal to dp[target]. Then the minimum weight of the stone remaining after the collision is (sum - dp[target]) - dp[target].

  • class Solution {
          
          
    public:
        int lastStoneWeightII(vector<int>& stones) {
          
          
            int sum=0;
            for(int i:stones){
          
          
                sum += i;
            }
            int target = sum / 2;
            int dp[1501] = {
          
          0};
            for(int i=0;i<stones.size();i++){
          
          
                for(int j=target;j>=stones[i];j--){
          
          
                    dp[j]=max(dp[j],dp[j-stones[i]]+stones[i]);
                }
            }
            return sum-dp[target]-dp[target];
        }
    };
    
  • Time complexity: O(m × n), m is the total weight of the stone (half of the total weight to be precise), n is the number of stone blocks; space complexity: O(m)

  • First of all, among all the problems with backpacks, the 01 backpack is the most basic, and other backpacks are also slightly modified based on the 01 backpack.

    • Insert image description here
  • Determine the meaning of the dp array and the subscripts: dp[i][j] means that if you take any item with the subscript [0-i] and put it into a backpack with a capacity of j, what is the maximum total value.

  • Determine the recursion formula: dp[i][j] = max(dp[i - 1] [j], dp[i - 1] [j - weight[i]] + value[i]);

  • How to initialize dp array

  • Determine the traversal order: 01 In the traversal order of the backpack two-dimensional dp array, the outer layer traverses items, the inner layer traverses backpack capacity and the outer layer traverses backpack capacity, and the inner layer traverses items are all possible!

  • Example: The maximum weight of the backpack is 4. The items are: [Item 0; 1; 15] [Item 1; 3; 20] [Item 2; 4; 30] The corresponding dp array values ​​are as shown in the figure:

    • Insert image description here

Item:Item title

  • You are given an array of non-negative integers nums and an integer target . Add '+' or '-' before each integer in the array, and then concatenate all the integers to construct an expression< /span>target. that can be constructed by the method above that evaluate to expressions: For example, nums = [2, 1], you can add '+' before 2, add '-' before 1, and then concatenate them to get the expression "+2-1". Returns the number of distinct

  • Assuming that the sum of addition is x, then the sum corresponding to subtraction is sum - x. So what we require is x - (sum - x) = target; Several methods. The x here is the backpack capacity we require later.

  • Determine the meaning of the dp array and subscripts: dp[j] means: filling a bag with a volume as large as j (including j), there are dp[j] methods; in fact, you can also use a two-dimensional dp array to solve this problem, dp[i ] [j]: Using nums[i] with subscript [0, i] can fill up a package with a capacity as large as j (including j). There are dp[i] [j] methods.

  • Determine the recursion formula: As long as nums[i] is obtained, there are dp[j - nums[i]] ways to make dp[j].

  • How to initialize the dp array: During initialization, dp[0] must be initialized to 1, because dp[0] is the origin of all recursive results in the formula. If dp[0] is 0, the recursive results will be 0.

  • class Solution {
          
          
    public:
        int findTargetSumWays(vector<int>& nums, int target) {
          
          
            int sum=0;
            for(int i:nums){
          
          
                sum+=i;
            }
            if(abs(target)>sum){
          
          
                return 0;
            }
            if((target+sum) & 1){
          
          
                return 0;
            }
            int left=(target+sum)/2;
            vector<int> dp(left+1,0);
            dp[0]=1;
            for(int i=0;i<nums.size();i++){
          
          
                for(int j=left;j>=nums[i];j--){
          
          
                    dp[j]+=dp[j-nums[i]];
                }
            }
            return dp[left];
        }
    };
    
  • Time complexity: O(n × m), n is a positive number, m is the backpack capacity; Space complexity: O(m), m is the backpack capacity

  • class Solution {
          
          
    public:
        int count=0;
        void track(vector<int> &nums,int target,int sum,int start){
          
          
            if(start==nums.size()){
          
          
                if(sum==target){
          
          
                    count++;
                }
            }else{
          
          
                track(nums,target,sum+nums[start],start+1);
                track(nums,target,sum-nums[start],start+1);
            }
    
        } 
        int findTargetSumWays(vector<int>& nums, int target) {
          
          
            track(nums,target,0,0);
            return count;
        }
    };//回溯
    
  • Time efficiency: O ( 2 n ) O(2^n) O(2n), in which n is the number pair nums \textit{nums} The length of nums. Backtracking requires traversing all the different expressions, there are 2 n 2^n 2n different expressions, each expression takes O(1) time to evaluate, so the total time complexity is . Space complexity: O(n), where n is the length of the array nums. The space complexity mainly depends on the stack space of the recursive call, and the depth of the stack does not exceed n.

  • Constructing a two-dimensional array for storage is easier to understand:

    • The sum of the elements of the array is sum, the sum of the elements with - sign is neg, then the sum of the remaining elements with + added is sum−neg, and the result of the expression is (sumneg)−neg< a i=6>=sum−2⋅neg=;target
    • Since the elements in the array nums are all non-negative integers, neg must also be a non-negative integer, so the premise for the above formula to hold is that sum−target is a non-negative even number. If this condition is not met, 0 can be returned directly.
    • If the above formula is true, the problem is transformed into selecting a number of elements in the array nums so that the sum of these elements is equal to neg, and calculating the number of options for selecting elements. We can use dynamic programming to solve it.
    • Define the two-dimensional array dp, where dp [ i ] [ j ] \textit{dp}[i][j] dp[i][ j] means selecting elements from the first i numbers in the array nums so that the sum of these elements is equal to j. Assuming that the length of the array nums is n, the final answer is $ \textit{dp}[n][\textit{neg}]$.
  • class Solution {
          
          
    public:
        int findTargetSumWays(vector<int>& nums, int target) {
          
          
            int sum=0;
            for(int &num:nums){
          
          
                sum+=num;
            }
            int diff=sum-target;
            if(diff<0 || diff%2!=0){
          
          
                return 0;
            }
            int n=nums.size(),neg=diff/2;
            vector<vector<int>> dp(n+1,vector<int>(neg+1));
            dp[0][0]=1;
            for(int i=1;i<=n;i++){
          
          
                int temp=nums[i-1];
                for(int j=0;j<=neg;j++){
          
          
                    dp[i][j]=dp[i-1][j];
                    if(j>=temp){
          
          
                        dp[i][j]+=dp[i-1][j-temp];
                    }
                }
            }
            return dp[n][neg];
        }
    };
    

Problem:Ichiwa Zero

  • You are given a binary string array strs and two integers m and n . Please find and return the length of the largest subset of strs in which has at most < a i=7> and . The set is of the set if all elements of are also elements of a> . subsetm0n1xyxy

  • **The elements in the strs array in this question are items, and each item is one! **Determine the meaning of the dp array (dp table) and the subscripts:dp[i][j]: The size of the largest subset of strs with at most i 0s and j 1s is dp [i][j].

  • Determine the recursion formula: dp[i][j] can be deduced from the string in the previous strs. The string in strs has zeroNum 0s and oneNum 1s. dp[i][j] can be dp[i - zeroNum] [j - oneNum] + 1. Then we take the maximum value of dp[i][j] during the traversal process. So the recursive formula: dp[i][j] = max(dp[i][j], dp[i - zeroNum] [j - oneNum] + 1); Recall the recursive formula of the 01 backpack: dp[j ] = max(dp[j], dp[j - weight[i]] + value[i]); You will find that the zeroNum and oneNum of the string are equivalent to the weight of the item (weight[i]), and the string itself The number is equivalent to the value of the item (value[i]).

  • How to initialize dp array to 0

  • class Solution {
          
          
    public:
        int findMaxForm(vector<string>& strs, int m, int n) {
          
          
            vector<vector<int>> dp(m+1,vector<int>(n+1,0));
            for(string str:strs){
          
          
                int one_count=0,zero_count=0;
                for(char c:str){
          
          
                    if(c=='0'){
          
          
                        zero_count++;
                    }else{
          
          
                        one_count++;
                    }
                }
                for(int i=m;i>=zero_count;i--){
          
          
                    for(int j=n;j>=one_count;j--){
          
          
                        dp[i][j] = max(dp[i-zero_count][j-one_count]+1,dp[i][j]);
                    }
                }
            }
            return dp[m][n];
        }
    };
    
  • Time complexity: O(kmn), k is the length of strs; space complexity: O(mn). The classic knapsack problem only has one different capacity. This problem has two capacities, namely the upper limit of the number of 0s and 1s in the selected string subset.

Complete backpack

  • There are N items and a backpack that can carry a maximum weight of W. The weight of the i-th item is weight[i], and the obtained value is value[i]. There are an infinite number of each item (that is, it can be put into the backpack multiple times). . Find out which items should be put into the backpack with the largest total value. The only difference between the complete backpack problem and the 01 backpack problem is that there are infinite pieces of each item.

Title:Zero 钱兑换 II

  • Give you an integer array coins to represent coins of different denominations, and another integer amount to represent the total amount. Please calculate and return the number of coin combinations that can make up the total amount. If no coin combination can add up to the total amount, return 0 . Suppose there are an infinite number of coins of each denomination. The question data ensures that the result conforms to a 32-bit signed integer.

  • But this question is different from a pure complete backpack. **A pure complete backpack is about what is the maximum value of the backpack, while this question is about the number of item combinations required to make up the total amount! **Note that the title description refers to the number of coin combinations that make up the total amount. Combination does not emphasize the order between elements, while arrangement emphasizes the order between elements. .

  • Determine the meaning of the dp array and the subscript: dp[j]: The number of currency combinations that make up the total amount j is dp[j]

  • Determine the recursion formula: dp[j] is the sum of all dp[j - coins[i]] (considering the case of coins[i]). So the recursion formula: dp[j] += dp[j - coins[i]];

  • So the recursive formula: dp[j] += dp[j - coins[i]]; first, dp[0] must be 1, and dp[0] = 1 is the basis of the recursive formula. If dp[0] = 0, all subsequent derived values ​​will be 0. dp[0]=1 also illustrates a situation: if coins[i] is selected, that is, j-coins[i] == 0 means that this coin can be selected, and dp[0] is 1 means there is such a selection method to only select coins[i].

  • class Solution {
          
          
    public:
        int change(int amount, vector<int>& coins) {
          
          
            vector<int> dp(amount+1,0);
            dp[0]=1;
            for(int i=0;i<coins.size();i++){
          
          
                for(int j=coins[i];j<=amount;j++){
          
          
                    dp[j]+=dp[j-coins[i]];
                }
            }
            // for(auto i:dp){
          
          
            //     cout<<i<<endl;
            // }
            return dp[amount];
        }
    };
    
  • Time complexity: O(mn), where m is the amount and n is the length of coins; space complexity: O(m)

  • When there are several ways to fill a backpack, it is very important to understand the order of traversal.

    • If you want to find the number of combinations, the outer for loop traverses the items, and the inner for loop traverses the backpack.
    • If you want to find the number of permutations, the outer for loop traverses the backpack, and the inner for loop traverses the items.

Problem:Synthesis Ⅳ

  • Give you an array of different integersnums , and a target integer target . Please find and return the number of element combinations whose sum is from nums. The question data ensures that the answer fits within the 32-bit integer range. target

  • The description of this question says that it is about finding combinations, but it also says that combinations with the same elements in different orders can be counted as two combinations. **In fact, it is about asking for permutations! **The combination does not emphasize the order, (1,5) and (5,1) are the same combination. Arrangement emphasizes order, (1,5) and (5,1) are two different arrangements. The essence is that this question seeks the sum of permutations, and only the number of permutations, not all permutations. If you list all the permutations, you can only use the backtracking algorithm to search .

  • Determine the meaning of the dp array and the subscript:dp[i]: The number of permutations that make up the target positive integer i is dp[i]

  • Determine the recursion formula: dp[i] (considering nums[j]) can be derived from dp[i - nums[j]] (not considering nums[j]).

  • How to initialize the dp array: Because of the recursive formula dp[i] += dp[i - nums[j]], dp[0] must be initialized to 1, so that there will be a numerical basis when recursing other dp[i] .

  • Determine the order of traversal: the number can be unlimited, indicating that this is a complete backpack. There is no limit to the number of items that can be used, indicating that this is a complete backpack.

    • If you want to find the number of combinations, the outer for loop traverses the items, and the inner for loop traverses the backpack.
    • If you want to find the number of permutations, the outer for loop traverses the backpack, and the inner for loop traverses the items.
  • Use the example in the example to deduce:

    • Insert image description here
  • class Solution {
          
          
    public:
        int combinationSum4(vector<int>& nums, int target) {
          
          
            vector<int> dp(target+1,0);
            dp[0]=1;
            for(int i=0;i<target+1;i++){
          
          
                for(int j=0;j<nums.size();j++){
          
          
                    if(i-nums[j]>=0 && dp[i] < INT_MAX-dp[i-nums[j]]){
          
          
                        // C++测试用例有两个数相加超过int的数据,所以需要在if里加上dp[i] < INT_MAX - dp[i - num]。
                        dp[i] += dp[i-nums[j]];
                    }
                }
            }
            return dp[target];
        }
    };
    

Guess you like

Origin blog.csdn.net/weixin_43424450/article/details/134149144