前些日子有QQ好友發給我下面這個問題:java
啤酒2塊錢1瓶,4個蓋換一瓶,2個空瓶換一瓶,問10塊錢能夠喝多少瓶。函數
當時沒有時間算這個問題(其實就是懶得動筆和動腦子),但這幾天又老想着這個問題,因此今天決定寫段代碼解決一下。code
一開始,因爲沒把題目琢磨太明白,因而用了具備暴力色彩的遞歸方式去算這道題,考慮每一種交換順序,Java代碼以下:遞歸
import java.util.Stack; public class Solution { int countOfDrinked; Stack<String> stackStep = new Stack<String>(); String logOutputMax = ""; int priceEachBottle = 2; //一瓶酒多少錢 int bottleCanChange = 2; //多少瓶子能換一瓶酒 int capCanChange = 4; //多少瓶蓋能換一瓶酒 public int countOfDrink(int money) { countOfDrinked = 0; stackStep.clear(); logOutputMax = ""; calcMax(money, 0, 0, 0); System.out.println(logOutputMax); return countOfDrinked; } private void calcMax(int money, int bottle, int cap, int drinked) { if (money >= priceEachBottle) { String log = ""; log += "動做:買入啤酒, "; log += "剩餘金錢: " + (money - priceEachBottle) + ", "; log += "剩餘酒瓶:" + (bottle + 1) + ", "; log += "剩餘瓶蓋:" + (cap + 1) + ", "; log += "已喝啤酒:" + (drinked + 1); stackStep.push(log); calcMax(money - priceEachBottle, bottle + 1, cap + 1, drinked + 1); stackStep.pop(); } if (bottle >= bottleCanChange) { String log = ""; log += "動做:酒瓶換酒, "; log += "剩餘金錢: " + (money) + ", "; log += "剩餘酒瓶:" + (bottle - bottleCanChange + 1) + ", "; log += "剩餘瓶蓋:" + (cap + 1) + ", "; log += "已喝啤酒:" + (drinked + 1); stackStep.push(log); calcMax(money, bottle - bottleCanChange + 1, cap + 1, drinked + 1); stackStep.pop(); } if (cap >= capCanChange) { String log = ""; log += "動做:瓶蓋換酒, "; log += "剩餘金錢: " + (money) + ", "; log += "剩餘酒瓶:" + (bottle + 1) + ", "; log += "剩餘瓶蓋:" + (cap - capCanChange + 1) + ", "; log += "已喝啤酒:" + (drinked + 1); stackStep.push(log); calcMax(money, bottle + 1, cap - capCanChange + 1, drinked + 1); stackStep.pop(); } if (money < 2 && bottle < 2 && cap < 4) { if (drinked > countOfDrinked) { countOfDrinked = drinked; logOutputMax = String.join(";\n", stackStep); } } } }
建立一個Main函數,代碼以下:io
class DrinkingProblem { public static void main(String[] args) { Solution solution = new Solution(); System.out.println(solution.countOfDrink(10)); } }
輸出結果以下:ast
動做:買入啤酒, 剩餘金錢: 8, 剩餘酒瓶:1, 剩餘瓶蓋:1, 已喝啤酒:1; 動做:買入啤酒, 剩餘金錢: 6, 剩餘酒瓶:2, 剩餘瓶蓋:2, 已喝啤酒:2; 動做:買入啤酒, 剩餘金錢: 4, 剩餘酒瓶:3, 剩餘瓶蓋:3, 已喝啤酒:3; 動做:買入啤酒, 剩餘金錢: 2, 剩餘酒瓶:4, 剩餘瓶蓋:4, 已喝啤酒:4; 動做:買入啤酒, 剩餘金錢: 0, 剩餘酒瓶:5, 剩餘瓶蓋:5, 已喝啤酒:5; 動做:酒瓶換酒, 剩餘金錢: 0, 剩餘酒瓶:4, 剩餘瓶蓋:6, 已喝啤酒:6; 動做:酒瓶換酒, 剩餘金錢: 0, 剩餘酒瓶:3, 剩餘瓶蓋:7, 已喝啤酒:7; 動做:酒瓶換酒, 剩餘金錢: 0, 剩餘酒瓶:2, 剩餘瓶蓋:8, 已喝啤酒:8; 動做:酒瓶換酒, 剩餘金錢: 0, 剩餘酒瓶:1, 剩餘瓶蓋:9, 已喝啤酒:9; 動做:瓶蓋換酒, 剩餘金錢: 0, 剩餘酒瓶:2, 剩餘瓶蓋:6, 已喝啤酒:10; 動做:酒瓶換酒, 剩餘金錢: 0, 剩餘酒瓶:1, 剩餘瓶蓋:7, 已喝啤酒:11; 動做:瓶蓋換酒, 剩餘金錢: 0, 剩餘酒瓶:2, 剩餘瓶蓋:4, 已喝啤酒:12; 動做:酒瓶換酒, 剩餘金錢: 0, 剩餘酒瓶:1, 剩餘瓶蓋:5, 已喝啤酒:13; 動做:瓶蓋換酒, 剩餘金錢: 0, 剩餘酒瓶:2, 剩餘瓶蓋:2, 已喝啤酒:14; 動做:酒瓶換酒, 剩餘金錢: 0, 剩餘酒瓶:1, 剩餘瓶蓋:3, 已喝啤酒:15 15
經過觀察輸出結果並研究題目,我發現了下面幾條規律:class
一、兩塊錢能夠轉換成一瓶酒(含一個瓶和一個蓋),這個過程是不可逆的,因此能夠先把錢優先換成酒import
二、兩個酒瓶能夠轉換爲一個酒瓶和一個瓶蓋,四個瓶蓋能夠轉換爲一個酒瓶和一個瓶蓋,酒瓶和瓶蓋雖然存在着轉換關係,但這種轉換關係也是不可逆的,酒瓶和瓶蓋都只會越換越少,而且不管交換順序如何改變,從一個狀態轉到另外一個狀態所需花費的步驟數是同樣的。List
這就給上面方法的改進提供了空間,因而我新寫了一個代碼,發現代碼運行的結果是同樣的:方法
import java.util.LinkedList; public class Solution { LinkedList<String> linkedList = new LinkedList<String>(); int priceEachBottle = 2; //一瓶酒多少錢 int bottleCanChange = 2; //多少瓶子能換一瓶酒 int capCanChange = 4; //多少瓶蓋能換一瓶酒 public int countOfDrink(int money) { linkedList.clear(); calcMax(money, 0, 0, 0); System.out.println(String.join(";\n", linkedList)); return linkedList.size(); } private void calcMax(int money, int bottle, int cap, int drinked) { while (money >= priceEachBottle) { String log = ""; log += "動做:買入啤酒, "; log += "剩餘金錢: " + (money - priceEachBottle) + ", "; log += "剩餘酒瓶:" + (bottle + 1) + ", "; log += "剩餘瓶蓋:" + (cap + 1) + ", "; log += "已喝啤酒:" + (drinked + 1); linkedList.addLast(log); money -= priceEachBottle; bottle++; cap++; drinked++; } while (true) { if (bottle >= bottleCanChange) { String log = ""; log += "動做:酒瓶換酒, "; log += "剩餘金錢: " + (money) + ", "; log += "剩餘酒瓶:" + (bottle - bottleCanChange + 1) + ", "; log += "剩餘瓶蓋:" + (cap + 1) + ", "; log += "已喝啤酒:" + (drinked + 1); linkedList.addLast(log); bottle = bottle - bottleCanChange + 1; cap++; drinked++; } else if (cap >= capCanChange) { String log = ""; log += "動做:瓶蓋換酒, "; log += "剩餘金錢: " + (money) + ", "; log += "剩餘酒瓶:" + (bottle + 1) + ", "; log += "剩餘瓶蓋:" + (cap - capCanChange + 1) + ", "; log += "已喝啤酒:" + (drinked + 1); linkedList.addLast(log); bottle++; cap = cap - capCanChange + 1; drinked++; } else { break; } } } }
使用同一個Main函數運行便可,運行結果以下:
動做:買入啤酒, 剩餘金錢: 8, 剩餘酒瓶:1, 剩餘瓶蓋:1, 已喝啤酒:1; 動做:買入啤酒, 剩餘金錢: 6, 剩餘酒瓶:2, 剩餘瓶蓋:2, 已喝啤酒:2; 動做:買入啤酒, 剩餘金錢: 4, 剩餘酒瓶:3, 剩餘瓶蓋:3, 已喝啤酒:3; 動做:買入啤酒, 剩餘金錢: 2, 剩餘酒瓶:4, 剩餘瓶蓋:4, 已喝啤酒:4; 動做:買入啤酒, 剩餘金錢: 0, 剩餘酒瓶:5, 剩餘瓶蓋:5, 已喝啤酒:5; 動做:酒瓶換酒, 剩餘金錢: 0, 剩餘酒瓶:4, 剩餘瓶蓋:6, 已喝啤酒:6; 動做:酒瓶換酒, 剩餘金錢: 0, 剩餘酒瓶:3, 剩餘瓶蓋:7, 已喝啤酒:7; 動做:酒瓶換酒, 剩餘金錢: 0, 剩餘酒瓶:2, 剩餘瓶蓋:8, 已喝啤酒:8; 動做:酒瓶換酒, 剩餘金錢: 0, 剩餘酒瓶:1, 剩餘瓶蓋:9, 已喝啤酒:9; 動做:瓶蓋換酒, 剩餘金錢: 0, 剩餘酒瓶:2, 剩餘瓶蓋:6, 已喝啤酒:10; 動做:酒瓶換酒, 剩餘金錢: 0, 剩餘酒瓶:1, 剩餘瓶蓋:7, 已喝啤酒:11; 動做:瓶蓋換酒, 剩餘金錢: 0, 剩餘酒瓶:2, 剩餘瓶蓋:4, 已喝啤酒:12; 動做:酒瓶換酒, 剩餘金錢: 0, 剩餘酒瓶:1, 剩餘瓶蓋:5, 已喝啤酒:13; 動做:瓶蓋換酒, 剩餘金錢: 0, 剩餘酒瓶:2, 剩餘瓶蓋:2, 已喝啤酒:14; 動做:酒瓶換酒, 剩餘金錢: 0, 剩餘酒瓶:1, 剩餘瓶蓋:3, 已喝啤酒:15 15
能夠看出,其實兩個方法的輸出結果是同樣的,但第二種方法和第一種比,省去了不少沒必要要的計算步驟。
若是不須要顯示步驟,那麼稍微概括一下幾回不一樣輸入的結果(固然你也能夠選擇本身證實一下),能夠總結出一個公式:
設你有n塊錢,n>=4時,你能夠喝的啤酒數爲:2n-5(n爲偶數時),2n-7(n爲奇數時)
使用這個公式,應該是解決本題最快的方式了。
END