酒瓶與瓶蓋換酒問題 - 10塊錢能夠喝多少瓶酒

前些日子有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

相關文章
相關標籤/搜索