Last Stone Weight II
Last Stone Weight II
š§© Problem Statement
You are given an array of integers stones where stones[i] is the weight of the i-th 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. - If
x != y, the stone of weightxis destroyed, and the stone of weightyhas new weighty - 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:
- Combine 2 and 4 to get 2, so the array converts to [2,7,1,8,1] then,
- Combine 7 and 8 to get 1, so the array converts to [2,1,1,1] then,
- Combine 2 and 1 to get 1, so the array converts to [1,1,1] then,
- 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
š Approaches
1. Dynamic Programming (Subset Sum Variation)
The problem effectively asks to partition the stones into two groups $S_1$ and $S_2$ such that $|S_1 - S_2|$ is minimized.
Why? Because smashing two stones $x$ and $y$ ($y > x$) results in $y - x$. Eventually, every stone is either added or subtracted from the final result.
We want to minimize $TotalSum - 2 \cdot SubsetSum$, where $SubsetSum \le TotalSum/2$.
This transforms the problem into finding the largest SubsetSum that is $\le TotalSum/2$.
This is essentially the 0/1 Knapsack Problem (or Subset Sum) with capacity TotalSum/2.
Algorithm:
- Calculate
sum = total weight. - Target capacity
C = sum / 2. - Use a boolean DP array
dpof sizeC + 1to track reachable sums.
dp[0] = true.- For each stone
s: - Update
dpbackwards.dp[i] = dp[i] || dp[i - s].
- Find the largest
isuch thatdp[i]is true. - Result is
sum - 2 * i.
ā³ Time & Space Complexity
- Time Complexity: $O(N \cdot S)$, where $N$ is number of stones and $S$ is the total sum.
- Space Complexity: $O(S)$ for the DP array.
š Code Implementations
C++
#include <vector>
#include <numeric>
#include <algorithm>
using namespace std;
class Solution {
public:
int lastStoneWeightII(vector<int>& stones) {
int totalSum = accumulate(stones.begin(), stones.end(), 0);
int target = totalSum / 2;
vector<bool> dp(target + 1, false);
dp[0] = true;
for (int stone : stones) {
for (int j = target; j >= stone; --j) {
if (dp[j - stone]) {
dp[j] = true;
}
}
}
for (int i = target; i >= 0; --i) {
if (dp[i]) {
return totalSum - 2 * i;
}
}
return 0;
}
};
Python
from typing import List
class Solution:
def lastStoneWeightII(self, stones: List[int]) -> int:
total_sum = sum(stones)
target = total_sum // 2
# dp set containing all achievable sums <= target
dp = {0}
for stone in stones:
new_sums = set()
for s in dp:
if s + stone <= target:
new_sums.add(s + stone)
dp.update(new_sums)
max_s1 = max(dp)
return total_sum - 2 * max_s1
Java
class Solution {
public int lastStoneWeightII(int[] stones) {
int totalSum = 0;
for (int s : stones) totalSum += s;
int target = totalSum / 2;
boolean[] dp = new boolean[target + 1];
dp[0] = true;
for (int stone : stones) {
for (int j = target; j >= stone; j--) {
if (dp[j - stone]) {
dp[j] = true;
}
}
}
for (int i = target; i >= 0; i--) {
if (dp[i]) {
return totalSum - 2 * i;
}
}
return 0;
}
}
š Real-World Analogy
Tug of War:
You want to divide a group of people into two teams for Tug of War such that the strength difference is minimized. Smashing stones is like opposing forces canceling each other out.
šÆ Summary
ā
Minimize Difference: Equivalent to maximizing one subset's sum up to Total/2.
ā
Subset Sum Reused: Same logic as Partition Equal Subset Sum, just different output interpretation.
#include <iostream>
#include <numeric>
#include <vector>
using namespace std;
class Solution {
public:
int lastStoneWeightII(vector<int> &stones) {
int totalSum = accumulate(stones.begin(), stones.end(), 0);
int target = totalSum / 2;
vector<bool> dp(target + 1, false);
dp[0] = true;
for (int stone : stones) {
for (int j = target; j >= stone; --j) {
if (dp[j - stone]) {
dp[j] = true;
}
}
}
for (int i = target; i >= 0; --i) {
if (dp[i]) {
return totalSum - 2 * i;
}
}
return 0;
}
};
int main() {
Solution sol;
vector<int> stones1 = {2, 7, 4, 1, 8, 1};
cout << "Test Case 1: " << sol.lastStoneWeightII(stones1) << endl; // Expect 1
vector<int> stones2 = {31, 26, 33, 21, 40};
cout << "Test Case 2: " << sol.lastStoneWeightII(stones2) << endl; // Expect 5
return 0;
}