Last Stone Weight II
Find minimum absolute difference between stones
You are given an array of integers stones where stones[i] is the weight of the ith stone.
We are playing a game with the stones. On each turn, we choose any two stones and smash them together. Suppose the stones have weights x and y with x <= y. The result of this smash is:
If x == y, both stones are destroyed, and If x != y, the stone of weight x is destroyed, and the stone of weight y has new weight y - x. At the end of the game, there is at most one stone left.
Return the smallest possible weight of the left stone. If there are no stones left, return 0.
Example 1:
Input: stones = [2,7,4,1,8,1] Output: 1 Explanation: We can combine 2 and 4 to get 2, so the array converts to [2,7,1,8,1] then, we can combine 7 and 8 to get 1, so the array converts to [2,1,1,1] then, we can combine 2 and 1 to get 1, so the array converts to [1,1,1] then, we can combine 1 and 1 to get 0, so the array converts to [1], then that's the optimal value. Example 2:
Input: stones = [31,26,33,21,40] Output: 5
Solution
We start from zero index for base condition we check when all elements are checked we return absolute difference between w1 & w2.
else we pick current Weight in w1 or in w2 whichever gives minimum we'll return that.
var lastStoneWeightII = function(stones) {
let n= stones.length;
let memo = new Map();
function recurr(index,w1,w2){
let key=`${index}#${w1}#${w2}`
if(index===stones.length ){
return Math.abs(w1-w2)
}
if(memo.has(key)) return memo.get(key);
const currWeight= stones[index];
let w1pick= recurr(index+1, w1+ currWeight, w2);
let w2pick= recurr(index+1,w1, w2+currWeight)
const min= Math.min(w1pick,w2pick )
memo.set(key,min);
return min
}
return recurr(0,0,0)
};
Tabulation
This document explains the dynamic programming (DP) approach used to partition an array into two subsets such that the absolute difference between their sums is minimized.
1. How the Algorithm Works
The problem asks us to divide an array into two subsets, $S1$ and $S2$.
We know that the sum of the entire array is totalSum. Therefore, the sum of the second subset is naturally derived from the first:
$S2 = totalSum - S1$.
The absolute difference between the subsets is: $\vert{}S2 - S1\vert{} = \vert{}(totalSum - S1) - S1\vert{} = \vert{}totalSum - 2 \cdot S1\vert{}$.
To minimize this difference, we just need to find all possible valid sums that a subset ($S1$) can form. Then, we look for the valid $S1$ that is closest to totalSum / 2.
Code Breakdown
- Calculate
totalSum: We first find the sum of all elements in the array. - Create the DP Table: We create a 2D array
dpof sizen(number of elements) bytotalSum + 1.dp[i][target]istrueif a subset with sumtargetcan be formed using elements from index0toi. Otherwise, it'sfalse.
- Base Cases:
dp[i][0] = true: A target sum of0is always possible by selecting an empty subset.dp[0][arr[0]] = true: Using only the first element (arr[0]), the only non-zero sum we can form is the value ofarr[0]itself.
- DP Transitions: For every other element
iand target sumtarget, we have two choices:- Don't take it (
notTake): The target sum was already achievable with the previous elements (dp[i-1][target]). - Take it (
take): The target sum is achievable by adding the current element to a previously formed sum (dp[i-1][target - arr[i]]). - If either choice is
true, thendp[i][target]becomestrue.
- Don't take it (
- Find the Minimum Difference: We check the last row of our DP table (
dp[n-1]), which represents the sums achievable using all available elements. We iterates1from0tototalSum / 2and calculate the minimum possible difference.
2. Example Walkthrough & DP Table Visualization
Let's visualize the algorithm with the following array:
arr = [3, 1, 5]
n = 3totalSum = 3 + 1 + 5 = 9- The target sums we will check go from
0to9.
Initializing Base Cases
- Column
0is set totrue(T) because a sum of 0 is always possible. - Row
0(forarr[0] = 3) is set totrueattarget = 3.
Filling the DP Table
Step 1: Processing i = 1 (Current element: 1)
- We look at the previous row (
i=0). - We can carry over the existing true values (sum
0and sum3). - We can also add our current element (
1) to those existing sums to create sum1(0+1) and sum4(3+1).
Step 2: Processing i = 2 (Current element: 5)
- We look at the previous row (
i=1), where sums0, 1, 3, 4aretrue. - We carry those over.
- We also add
5to those existing sums to create sums5(0+5),6(1+5),8(3+5), and9(4+5).
The Final DP Table
Here is the state of the DP table after all iterations (T = true, F = false):
i (index) \ target sum | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 (val: 3) | T | F | F | T | F | F | F | F | F | F |
| 1 (val: 1) | T | T | F | T | T | F | F | F | F | F |
| 2 (val: 5) | T | T | F | T | T | T | T | F | T | T |
Calculating the Result
The algorithm now looks at the last row (i = 2).
We only need to check possible sums (s1) up to totalSum / 2, which is 9 / 2 = 4.5.
So, we check s1 for 0, 1, 2, 3, 4.
Looking at the last row for columns 0 to 4:
s1 = 0: valid (T). Difference =|9 - 2(0)| = 9s1 = 1: valid (T). Difference =|9 - 2(1)| = 7s1 = 2: invalid (F). Skip.s1 = 3: valid (T). Difference =|9 - 2(3)| = 3s1 = 4: valid (T). Difference =|9 - 2(4)| = 1
The minimum absolute difference is 1, achieved by partitioning the array into subset $S1 = {3, 1}$ (sum = 4) and subset $S2 = {5}$ (sum = 5).
function minSubsetSumDifference(arr) {
const n = arr.length;
let totalSum = 0;
for (let i = 0; i < n; i++) {
totalSum += arr[i];
}
// Create a 2D DP table (n x totalSum + 1)
const dp = Array.from({ length: n }, () => Array(totalSum + 1).fill(false));
// Base case: A target sum of 0 is always possible (by picking no elements)
for (let i = 0; i < n; i++) {
dp[i][0] = true;
}
// Base case: The first element can only form a sum equal to itself
if (arr[0] <= totalSum) {
dp[0][arr[0]] = true;
}
// Fill the DP table
for (let i = 1; i < n; i++) {
for (let target = 1; target <= totalSum; target++) {
// Option 1: Don't include the current element
let notTake = dp[i - 1][target];
// Option 2: Include the current element (if possible)
let take = false;
if (arr[i] <= target) {
take = dp[i - 1][target - arr[i]];
}
dp[i][target] = take || notTake;
}
}
// Find the minimum absolute difference by checking valid sums in the last row
let mini = Infinity;
for (let s1 = 0; s1 <= totalSum / 2; s1++) {
if (dp[n - 1][s1] === true) {
let s2 = totalSum - s1;
let diff = Math.abs(s2 - s1);
mini = Math.min(mini, diff);
}
}
return mini;
}