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 weightx
is destroyed, and the stone of weighty
has 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:
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
Example 3:
Input: stones = [1,2]
Output: 1
Constraints:
1 <= stones.length <= 30
1 <= stones[i] <= 100
這道題是之前那道 Last Stone Weight 的拓展,之前那道題說是每次取兩個最大的進行碰撞,問最后剩下的重量。而這里是可以任意取兩個石頭進行碰撞,並且需要最后剩余的重量最小,這種玩數組求極值的題十有八九都是用動態規划 Dynamic Programming 來做的。首先來考慮 dp 數組該如何定義,若是直接用 dp[i] 來表示區間 [0, i] 內的石頭碰撞后剩余的最小重量,狀態轉移方程將十分難推導,因為石子是任意選的,當前的 dp 值和之前的沒有太大的聯系。這里需要重新考慮 dp 數組的定義,這道題的解法其實挺難想的,需要轉換一下思維,雖說是求碰撞后剩余的重量,但實際上可以看成是要將石子分為兩堆,且盡可能讓二者的重量之和最接近。若分為的兩堆重量相等,則相互碰撞后最終將直接湮滅,剩余為0;若不相等,則剩余的重量就是兩堆石子的重量之差。這道題給的數據范圍是石子個數不超過 30 個,每個的重量不超過 100,這樣的話總共的石子重量不超過 3000,分為兩堆的話,每堆的重量不超過 1500。我們應該將 dp[i] 定義為數組中的石子是否能組成重量為i的一堆,數組大小設為 1501 即可,且 dp[0] 初始化為 true。這里的狀態轉移的思路跟之前那道 Coin Change 是很相似的,遍歷每個石頭,累加當前石頭重量到 sum,然后從 1500 和 sum 中的較小值開始遍歷(因為每堆的總重量不超過 1500),且i要大於 stone,小於當前石頭的i不需要更新,由於當前的石頭重量 stone 知道了,那么假如 i-stone 的 dp 值為 true 的話,則 dp[i] 也一定為 true。更新完成之后,從 sum/2 開始遍歷,假如其 dp 值為 true,則用總重量 sum 減去當前重量的2倍,就是二堆石頭重量的差值了,也就是碰撞后的剩余重量了,參見代碼如下:
class Solution {
public:
int lastStoneWeightII(vector<int>& stones) {
vector<bool> dp(1501);
dp[0] = true;
int sum = 0;
for (int stone : stones) {
sum += stone;
for (int i = min(1500, sum); i >= stone; --i) {
dp[i] = dp[i] || dp[i - stone];
}
}
for (int i = sum / 2; i >= 0; --i) {
if (dp[i]) return sum - 2 * i;
}
return 0;
}
};
Github 同步地址:
https://github.com/grandyang/leetcode/issues/1049
類似題目:
參考資料:
https://leetcode.com/problems/last-stone-weight-ii/