知識回顧
貪心演算法(greedy algorithm),又稱貪婪演算法。是一種在每一步選擇中都採取在當前狀態下最好或最優(即最有利)的選擇,從而希望導致結果是最好或最優的演算法。
貪心演算法在有最優子結構的問題中尤為有效。最優子結構的意思是區域性最優解能決定全域性最優解。簡單地說,問題能夠分解成子問題來解決,子問題的最優解能遞推到最終問題的最優解。
貪心演算法與動態規劃的不同在於它對每個子問題的解決方案都做出選擇,不能回退。動態規劃則會儲存以前的運算結果,並根據以前的結果對當前進行選擇,有回退功能。
貪心法可以解決一些最優化問題,如:求圖中的最小生成樹、求哈夫曼編碼……對於其他問題,貪心法一般不能得到我們所要求的答案。一旦一個問題可以通過貪心法來解決,那麼貪心法一般是解決這個問題的最好辦法。由於貪心法的高效性以及其所求得的答案比較接近最優結果,貪心法也可以用作輔助演算法或者直接解決一些要求結果不特別精確的問題。在不同情況,選擇最優的解,可能會導致辛普森悖論(Simpson's Paradox),不一定出現最優的解。
換酒問題
題目描述:
小區便利店正在促銷,用 N 個空酒瓶可以兌換一瓶新酒,你購入了 M 瓶酒。如果喝掉了酒瓶中的酒,那麼酒瓶就會變成空的,請你計算最多能喝到多少瓶酒?
示例1:
輸入:M = 9, N = 3
輸出:13
解釋:你可以用 3 個空酒瓶兌換 1 瓶酒。所以最多能喝到 9 + 3 + 1 = 13 瓶酒。
示例2:
輸入:M = 11, N = 3
輸出:16
解釋:你可以用 3 個空酒瓶兌換 1 瓶酒。所以最多能喝到 11 + 3 + 1 + 1 = 16 瓶酒。
問題分析:
喝完當前所有酒後得到的空瓶加上已有空瓶,最大限度的、貪心的兌換酒,依次類推,直到手上的空瓶不足以兌換出一瓶酒為止。
程式碼實現:
public static int drink(int m, int n) {
int drinked = m; // 已經喝到的酒數
int empty = m; // 空酒瓶的數量
while ((empty / n) != 0) {
int changed = empty / n; // 換酒
drinked += changed; // 將換的酒喝掉
empty = changed + empty % n; // 此時空酒瓶的數量
}
return drinked;
}
總結一下
當我們解決問題時,我們的切入點很重要,我們解決喝酒問題時切入的並不是起始點,而是從喝完所有你購入的酒後開始,為什麼要這樣呢?看圖可知,因為後續的操作才是貪心演算法的體現,每一個環節的操作都是一樣的。
- 第一步,看當前空酒瓶是否能夠兌換到酒,如果可以,執行下面的操作;如果不可以,問題解答完畢,返回已經喝到的酒數。
- 第二步,將當前的空酒瓶最大限度的、貪心的兌換酒。
- 第三步,喝掉所有兌換到的酒,得到空酒瓶。
- 第四步,統計當前空酒瓶的數量,兌換前剩餘的加上已經喝完的兌換的酒的空瓶,重複第一步。