These problems discussed below conform to one knapsack pattern. Using solutions to the knapsack problem and a minor modification, you can solve almost all of the following problems. We'll discuss this as we explain the code and the approach to these problems.
It is a standard \(0/1\) Knapsack Problem. Given a knapsack with \(W\) bound on the weight, \(p[n]\) profits for each item, \(w[n]\) weights of each item, find \(X \in \{0,1\}^n\) such that \(p^TX\) is maximized and \(w^T X \leq W\). \(X\) is a solution to the \(0/1\) knapsack problem.
Approach
Recursive approach
Suppose \(w= [1,3,4,5]\) and \(p = [1,4,5,7]\). Now we have \(4\) items, and we create a choice diagram. For each \(i \in [n]\) we have a choice whether to include that element \(i\) into the knapsack.
Following is the choice diagram for every element in the item list.
Recursive design should follow the syntax described below
classSolution{public://Function to return max value that can be put in knapsack of capacity W.intknapSack(intW,intwt[],intval[],intn){/* BASE CONDITION [smallest value input] */if(n<=0orW<=0){return0;}/* Choice Diagram */if(wt[n-1]<=W){returnstd::max(val[n-1]+knapSack(W-wt[n-1],wt,val,n-1),knapSack(W,wt,val,n-1));}// not include as capacity is lowerreturnknapSack(W,wt,val,n-1);}};
We now memoize the solution to use the dynamic programming paradigm.
General approach to memoize a recursive solution
First, we need to identify what are the values that are changing,
Make a vector<nested vector<int>> 1D, 2D. 3D depending upon the number of elements changing.
classSolution{public:vector<vector<int>>dp;booltableInitialized=false;//Function to return max value that can be put in knapsack of capacity W.intknapSack(intW,intwt[],intval[],intn){// base condition [smallest value input]if(nottableInitialized){dp=vector<vector<int>>(n+1,vector<int>(W+1,-1));tableInitialized=true;}if(dp[n][W]!=-1){returndp[n][W];}if(n<=0orW<=0){return0;}if(wt[n-1]<=W){dp[n][W]=std::max(val[n-1]+knapSack(W-wt[n-1],wt,val,n-1),knapSack(W,wt,val,n-1));returndp[n][W];}// not include as capacity is lowerdp[n][W]=knapSack(W,wt,val,n-1);returndp[n][W];}};
The following is a top-down approach for \(0/1\) knapsack
classSolution{public://Function to return max value that can be put in knapsack of capacity W.intknapSack(intW,intwt[],intval[],intn){intdp[n+1][W+1];memset(dp,-1,sizeof(dp));for(inti=0;i<W+1;i++){dp[0][i]=0;}for(inti=0;i<n+1;i++){dp[i][0]=0;}for(inti=1;i<n+1;i++){for(intj=1;j<W+1;j++){if(j>=wt[i-1]){dp[i][j]=std::max(dp[i-1][j],// not considering the i th elementdp[i-1][j-wt[i-1]]+val[i-1]// considering the i th element);}else{dp[i][j]=dp[i-1][j];// not considering the i th element}}}returndp[n][W];}};
Approach Identification
To identify a problem having a solution similar to knapsack is to find out if there are two of the following thing
an upper bound of some integer \(W\),
An array where we can choose to include or not include certain elements
Subset Sum
Problem Statement
Given an array of non-negative integers and a value sum, determine if there is a subset of the given set with a sum equal to the given sum
Example
Input: N = 6, arr[] = {3, 34, 4, 12, 5, 2}, sum = 9
Output: 1
Explanation: Here there exists a subset with sum = 9, 4+3+2 = 9.
classSolution{public:boolsubSetPossible=false;vector<vector<int>>dp;// Main functionboolisSubsetSum(vector<int>arr,intsum){intsize=arr.size();dp=vector<vector<int>>(sum+1,vector<int>(size+1,-1));// sum and index is changingrecursiveSubroutine(arr,sum,0,size);returnsubSetPossible;}voidrecursiveSubroutine(vector<int>&arr,intsum,inti,intsize){if(i==size)return;if(sum==0){subSetPossible=true;return;}if(arr[i]>sum){// do not take the element and move onrecursiveSubroutine(arr,sum,i+1,size);return;}else{recursiveSubroutine(arr,sum-arr[i],i+1,size);// takerecursiveSubroutine(arr,sum,i+1,size);// no take}}};
Now all left is to memoize the solution
My approach for memoization of the code up-above.
We find that sum, and i are two variable that is changing.
Goal for memoization is not to compute any precomputed function calls.
So we'll create a vector dp to store existing call results on a particular sum, i.
We need to change the function signature by returning a bool if dp[sum][i]` is true. This will solve sub-problems, and using that, we can compute the subset sum optimally using overlapping subproblems.
classSolution{public:boolsubSetPossible=false;vector<vector<int>>dp;boolisSubsetSum(vector<int>arr,intsum){intsize=arr.size();dp=vector<vector<int>>(sum+1,vector<int>(size+1,-1));// sum and index is changingreturnrecursiveSubroutine(arr,sum,0,size);}boolrecursiveSubroutine(vector<int>&arr,intsum,inti,intsize){if(i>=sizeandsum)returnfalse;if(sum==0){returntrue;}if(dp[sum][i]!=-1)returndp[sum][i];if(arr[i]>sum){// do not take the element and move onreturndp[sum][i]=recursiveSubroutine(arr,sum,i+1,size);}returndp[sum][i]=(recursiveSubroutine(arr,sum-arr[i],i+1,size)orrecursiveSubroutine(arr,sum,i+1,size));}};
classSolution{public:vector<vector<bool>>dp;boolisSubsetSum(vector<int>arr,intsum){intsize=arr.size();dp=vector<vector<bool>>(sum+1,vector<bool>(size+1,-1));// if sum = 0; then always possiblefor(inti=0;i<=size;i++){dp[0][i]=true;}// > 0 sum and no element is not possiblefor(inti=1;i<=sum;i++){dp[i][0]=false;}for(inti=1;i<=sum;i++){for(intj=1;j<=size;j++){// dp[i][j] till jth element is it possible to // get a subset with sum = i;if(arr[j-1]>i){// not possibledp[i][j]=dp[i][j-1];}else{dp[i][j]=dp[i][j-1]ordp[i-arr[j-1]][j-1];}}}returndp[sum][size];}};
Given an integer array nums, return true if you can partition the array into two subsets such that the sum of the elements in both subsets is equal or false otherwise.
Examples
Input: nums = [1,5,11,5]
Output: true
Explanation: The array can be partitioned as [1, 5, 5] and [11].
---
Input: nums = [1,2,3,5]
Output: false
Explanation: The array cannot be partitioned into equal sum subsets.
---
Approach
We have to divide the array into two parts to equal the total sum.
If the total sum is odd, then no way we can separate the two arrays.
Otherwise, we need to find if there exists a subset with target \(= \frac{\text{sum}}{2}\).
In the last problem we indicated if there exists a sub problem with isSubsetSum = true. Now for this problem we simply add true boolean outputs from subproblems. This way we can count how many times true is returned.
classSolution{public:intMOD=1e9+7;vector<vector<int>>dp;intperfectSum(intarr[],intn,intsum){dp=vector<vector<int>>(n+1,vector<int>(sum+1,0));for(inti=0;i<=n;i++){dp[i][0]=1;}for(inti=1;i<=n;i++){for(intj=0;j<=sum;j++){// TODO: if we set j = 1..sum there is a problemif(j-arr[i-1]>=0){dp[i][j]=(dp[i-1][j]+dp[i-1][j-arr[i-1]])%MOD;}else{dp[i][j]=dp[i-1][j];// dont consider the element}}}returndp[n][sum];}};
Minimum subset sum difference
Problem Statement
You are given an array arr containing n non-negative integers. Your task is to partition this array into two subsets such that the absolute difference between subset sums is minimum. You just need to find the minimum absolute difference considering any valid division of the array elements.
Approach
We need to divide the array into two parts \(s_1, s_2\) such that \(abs(\sum_{i} s_1[i] - \sum_{i} s_2[i])\) is minimized.
For each subset \(s_i\), the sum \(\sum_{j} s_i[j] \in \left[0, \sum_{i} A[i]\right]\). Where \(A\) is the main array.
Now instead of finding \(\displaystyle\min_{\forall s_1, s_2 \in 2^n} abs(s_1, s_2)\) we need to find \(\displaystyle\min_{\forall s_1 \in 2^n} abs(2s_1 - \sum_i A[i])\).
Now for each of the possible sum \(\sum_i s_1[i]\) we find this breaking creates least value of \(abs(2s_1 - \sum_i A[i])\).
typedefstructLoc{inti,j;}Loc;intminSubsetSumDifference(vector<int>&arr,intn){intminSum=0;intmaxSum=accumulate(arr.begin(),arr.end(),0);intminimumSubsetSumDifference=INT_MAX;intsize=arr.size();intsum=maxSum;vector<vector<bool>>dp(size+1,vector<bool>(sum+1,false));for(inti=0;i<=size;i++){dp[i][0]=true;}vector<Loc>locations;// store locations in the last array where// there is a subset s_1 possiblefor(inti=1;i<=size;i++){for(intj=1;j<=sum;j++){if(j-arr[i-1]>=0){dp[i][j]=dp[i-1][j]ordp[i-1][j-arr[i-1]];}else{dp[i][j]=dp[i-1][j];}if(i==sizeanddp[i][j]==1){Locloc;loc.i=i,loc.j=j;locations.push_back(loc);}}}// for each of the s_i find out the difference.for(autoloc:locations){inti=loc.i,j=loc.j;minimumSubsetSumDifference=std::min(minimumSubsetSumDifference,abs(2*j-maxSum));}returnminimumSubsetSumDifference;}
Number of subsets with given difference
Problem Statement
Given an array \(A\) and a difference diff = \(d\), find the number of subsets that array can be divided into so that each the difference between the two subset is the given \(d\).
Approach
We'll re-use the previous concepts in order to solve this problem at hand,
We need to break the array into two parts \(S_1, S_2\) such a way that \(\sum_i S_1[i] - \sum_i S_2[i] = d\) where \(d\) is the given difference. We need to find out the number of such pairs \(S_1, S_2\).
Observation: it is also known that \(\sum_i S_1[i] + \sum_i S_2[i] = \sum_i A[i]\).
If we add the two equation from up above we get the following \(2* \sum_i S_1[i] = d + \sum_i A[i]\). Hence \(\sum_i S_1[i] = \frac{d + \sum_i A[i]}{2}\)
Hence the question now becomes from How many \(S_1, S_2\) pairs possible for the difference = \(d\) to How many subsets possible with \(S_1 = j\) for some number \(j = \frac{d + \sum_i A[i]}{2}\).
#include<bits/stdc++.h>usingnamespacestd;classSolution{public:intMOD=1e9+7;intsumOfArray=0;vector<vector<int>>dp;intcountPartitions(intn,intd,vector<int>&arr){sumOfArray=0;for(inti:arr)sumOfArray+=i;intj=(d+sumOfArray)/2;buildDPTable(n,arr,j);returndp[n][j];}// build the dp tablevoidbuildDPTable(intn,vector<int>&arr,intsum){dp=vector<vector<int>>(n+1,vector<int>(sum+1,-1));for(inti=1;i<=sum;i++){dp[0][i]=0;// no solution for +ve sum and 0 element}for(inti=0;i<=n;i++){dp[i][0]=1;// empty subset for sum = 0}for(inti=1;i<=n;i++){for(intj=1;j<=sum;j++){dp[i][j]=dp[i-1][j];if(j-arr[i-1]>=0){dp[i][j]+=dp[i-1][j-arr[i-1]];}}}}};intmain(){Solutions;intn,d;cin>>n>>d;vector<int>a(n,0);for(inti=0;i<n;i++)cin>>a[i];cout<<s.countPartitions(n,d,a);}
You are given an integer array \(\textsf{nums}\) and an integer \(\textsf{target}\).
You want to build an expression out of nums by adding one of the symbols '+' and '-' before each integer in nums and then concatenate all the integers.
For example, if \(\textsf{nums} = [2, 1]\), you can add a '+' before 2 and a '-' before 1 and concatenate them to build the expression \(+2-1\).
Return the number of different expressions that you can build, which evaluates to target.
classSolution{private:intcountSubsetSumDifference(vector<int>&nums,intdifference){intsum=accumulate(nums.begin(),nums.end(),0);inttarget=(sum+difference)/2;if((sum+difference)%2orsum<difference)return0;if(target<0)return0;returnfindNumberOfSubsetWithTarget(nums,target);}intfindNumberOfSubsetWithTarget(vector<int>&nums,intsum){intn=nums.size();vector<vector<int>>dp(n+1,vector<int>(sum+1,0));// if sum = 0 then empty set is goodfor(inti=0;i<=n;i++){dp[i][0]=1;}for(inti=1;i<=n;i++){for(intj=0;j<=sum;j++){// starting from j = 0 because there can be element// 0 that can be added into the subsets to achieve 0 sum.if(j-nums[i-1]>=0){dp[i][j]=dp[i-1][j]+dp[i-1][j-nums[i-1]];}else{dp[i][j]=dp[i-1][j];}}}returndp[n][sum];}public:intfindTargetSumWays(vector<int>&nums,inttarget){if(nums.size()==1){// base caseif(abs(nums[0])==abs(target))return1;elsereturn0;}returncountSubsetSumDifference(nums,target);}};