酒瓶与瓶盖换酒问题 - 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

相关文章
相关标签/搜索