有一堆石頭,每塊石頭的重量都是正整數。
每一回合,從中選出任意兩塊石頭,然后將它們一起粉碎。假設石頭的重量分別為 x 和 y,且 x <= y。那么粉碎的可能結果如下:
如果 x == y,那么兩塊石頭都會被完全粉碎;
如果 x != y,那么重量為 x 的石頭將會完全粉碎,而重量為 y 的石頭新重量為 y-x。
最后,最多只會剩下一塊石頭。返回此石頭最小的可能重量。如果沒有石頭剩下,就返回 0。
示例:
輸入:[2,7,4,1,8,1]
輸出:1
解釋:
組合 2 和 4,得到 2,所以數組轉化為 [2,7,1,8,1],
組合 7 和 8,得到 1,所以數組轉化為 [2,1,1,1],
組合 2 和 1,得到 1,所以數組轉化為 [1,1,1],
組合 1 和 1,得到 0,所以數組轉化為 [1],這就是最優值。
提示:
1 <= stones.length <= 30
1 <= stones[i] <= 1000
題目不必多說,兩塊石頭相撞,找出剩下最小的。雖然看似給了很多石頭,其實我們可以將其分類融合成兩塊石頭,使這兩塊石頭質量盡可能的相近。
顯然最小的答案是0,也就是說兩個石頭一樣的重,為sumWeight / 2。那么我們現在開始嘗試尋找最接近sumWeight /2的一些石頭的集合。
到了石頭堆面前,我們手頭現在有一個sum/2大小的背包,現在要想辦法盡可能的將背包裝滿。
現在開始裝石頭。
拿到石頭后我們有兩種選擇,1:裝,2:不裝。這可能是廢話。
那么裝或者不裝是有判斷依據的,滿足條件了我們才會去裝石頭。
裝石頭的判斷依據顯然是讓背包越滿越好,然而可能我們裝入了石頭后,反而還不如之前不裝滿,這就需要我們繼續思考了。
這就會有新舊兩種狀態的矛盾。
新狀態是指裝石頭,舊狀態是指不裝石頭。
還有一個概念是最優解,是說在目前為止,你的背包里面裝了選擇最優策略所得到的石頭,也就是說你的背包里裝了目前為止所能拿到的最多的石頭。
那么新舊兩種狀態要去比較必然需要都同時處於最優解才可以進行比較。
而很顯然,舊的狀態是已經達到最優解得了。
那么我們需要讓新狀態也達到最優解。
既然已經放入了當前石頭,那么也就意味着要讓背包剩下的空間達到最優解。這也意味着我們需要重新去尋找剩余空間的最優解。
不過不用擔心,剩余空間的最優解我們已經在dp數組中已經存儲了,我們只需要去查看就可以。
dp數組是一個大小為背包大小的數組,每個數組空間都存儲了對應下標大小的最優解。也就是要用空間換時間的算法,將前面計算過的最優解
存儲下來。
對於我們拿到的每一塊石頭我們都要去對每一個可以容納下它的背包去進行最優解更新,這樣我們dp數組的每一個空間才能保證都是最優解。
代碼如下
1 public int lastStoneWeightII(int[] stones) { 2 int sum = 0; 3 for(int i : stones) 4 sum += i; 5 int[] dp = new int[sum/2+1]; 6 int maxSize = sum/2; 7 for(int i = 0; i < stones.length; i++){ 8 int curStones = stones[i]; 9 for(int j = maxSize; j >= curStones; j--){ 10 dp[j] = Math.max(dp[j], dp[j - curStones] + curStones); 11 } 12 } 13 return sum - dp[maxSize]*2; 14 }