10块钱到底能买多少酒?

这是我第一次写博客,写的是用编程解决一个很简单的数学问题,一个很经典的数学问题,买酒问题。

希望觉得简单的同志们嘴下留情可怜,对于新手们这是个比较不错的思维锻炼。

如果大家有一些类似的有趣数学问题可以给我留言,我会在后续的博客中选择一些能力以内的来实现它。微笑

问题描述:啤酒2块钱一瓶,两个瓶子可以再换一瓶酒,四个盖子可以再换一瓶酒。那么10块钱到底能够喝多少酒呢?

思路一:将所有的盖子,瓶子,酒以统一的单位表示。

1.酒两块钱一瓶。

2.四个盖子是能换一瓶酒。

3.两个瓶子能换一瓶酒。

那么我们得出这样一个公式。

以x表示一瓶酒的价格不包括瓶子和盖子。

以y表示瓶子的价格。

以z表示盖子的价格。

有下面几个等式

x+y+z=2

y=2z

2y=2

所以可以得知

x=0.5

y=1

z=0.5

那么到底能喝都少酒那就直接那所有的钱除以一瓶酒的钱,其中不包括盖子喝瓶子。

那么能喝多少酒呢?

10/0.5=20。

能喝20瓶酒!惊讶这么多?

然而我似乎忽略了一个问题。一个瓶子加两个盖子是换不了酒的

假如一个人最后剩下了两个盖子一个瓶子这种情况呢。

因为没到最后不知道到底会剩下几个瓶子几个盖子对吧。

接下来所剩瓶子我用w来表示

所剩下的盖子我用p来表示

那么w的取值范围:

w:[0,2)

p的取值范围:

p:[0,4)

但是w和p究竟是多少我们不知道。

有人会问为什么不考虑还剩下多少钱?

对。还有钱的问题。

钱用M来表示吧

M=10%2,

M=0。

所以我直接给忽略了。

介于w和p我们短时间也算不出来所以这个思路被我放弃了。尴尬(一开始没往这边想,在做好之后才想起还有这个思路)。

虽然这个思路不太行得通,但是他对我们验证结果还是有帮助的。我在思路2里面会介绍到。

思路二:穷举法

穷举法是编程里面比较高常用的方法,这个问题要编程算出来其实不难,只需要把买酒和换酒的步骤一步一步的从现就能得到正确答案,这也是编程解决这个问题的最直接的方法。

我说下思路。

1.用钱换酒。记录多少酒瓶和多少盖子以及还剩多少钱,喝了多少酒。

2.用盖子换酒。记录瓶子和盖子的变化,以及又喝了多少酒。(注意:因为在第一步把钱用掉之后所剩的钱根本买不了酒了,所以再也不必考虑钱的问题)。

3.用瓶子换酒。记录瓶子和盖子的变化,以及又喝了多少酒。

4.判断还能不能用瓶子或盖子换酒。

能:重复2,3步骤

不能:返回“到底喝了多少酒”。

下面是我的核心实现过程

        public void 买酒喝酒() {
            if (能不能用钱买酒())
                用钱买酒();
            if (能不能用盖子买酒())
                用盖子买酒();
            if (能不能用瓶子买酒())
                用瓶子买酒();
            if (能不能用钱买酒() || 能不能用盖子买酒() || 能不能用瓶子买酒())
                买酒喝酒();
        }

微笑方法名是汉字的,便于大家理解。对就是这么简单。还有其他部分的源码,我就不一一列出来了喜欢解决问题的初学者们可以尝试自己实现这里面的几个方法微笑。还有就是不用纠结于用什么语言,解决这些基本数学小问题什么语言都一样。

最后我得到的结果是这样的

10块钱喝了15瓶酒。

怎么验证呢?这个时候就用到思路一里面提到的内容。

纯酒单价:0.5

瓶子单价:1

盖子单价:0.5

我看看看这个结果的总价钱是不是10元!

15*0.5+1+3*0.5=10.0

这个结果是对的。

那么问题来了,有没有其他解?

这个我不知道,其实看透了本质是一个线性规划的问题。结果必定满足下面几个方程。

设:x为喝酒总数,y为最后剩下的瓶子数,z为最后剩下的盖子数

(一)x*0.5+y+z*0.5=10

(二)0<=y<2

(三)0<=z<4

然后我就疑惑了,

满足上面几个方程

还有这几个解。

x=16,

x=17,

x=18,

x=19,

x=20.

总觉得还有一些等式或不等式没有挖掘出来抓狂。算了,用计算机来验证。

难道是购买顺序又问题

于是将算法改成了这样

  public void 买酒喝酒() {
            if (能不能用钱买酒())
                用钱买酒();
            var rand = new Random();
            var next = rand.Next(0, 10);
            if (next % 2 == 0)
            {
                if (能不能用瓶子买酒())
                    用瓶子买酒();
                if (能不能用盖子买酒())
                    用盖子买酒();
            }
            else
            {
                if (能不能用盖子买酒())
                    用盖子买酒();
                if (能不能用瓶子买酒())
                    用瓶子买酒();
            }
            if (能不能用钱买酒() || 能不能用盖子买酒() || 能不能用瓶子买酒())
                买酒喝酒();
        }
购买的顺序是随机的。然后循环了一百次的得道唯一解都是

这样。

看来跟换酒的顺序没关系。

然后我的出的结论是,买酒的问题可能存在的最优解就是喝了15瓶酒。

思考:如果盖子,酒瓶和钱能够拼接使用又是什么结果呢?

这个问题在经过前一个问题的洗礼后看似复杂其实更加简单了。

理想结果是我花光了所有的钱,手里面只剩下一个瓶子和一个盖子。

为什么?因为假如我倒数第二次刚好够再买一瓶奶我手中必然会只剩下一个瓶子一个盖子。老板又不给我退。

所以我一瓶一瓶的买,买了第一瓶我的手里面会有一瓶,一盖,8块钱。我再加5毛就变成7.5块钱加上一个瓶子一个盖子。最后我手里面就会剩下一个瓶子和一个盖子。结果我喝了(10-1.5)/0.5=17瓶酒,手里面剩下一套瓶子。我也懒得去验证了。

最后附上源码:

using System;

namespace howmoney {
    class Program {
        static void Main() {
            for (int i = 0; i < 100; i++)
            {
                Console.WriteLine("----------------------------第{0}次买酒----------------------------------",i);
                算喝了几瓶 喝酒 = new 算喝了几瓶(10);
                喝酒.买酒喝酒();
                int 到底喝了多少瓶 = 喝酒.到底喝了几瓶();
                Console.WriteLine("老子到底喝了几瓶酒:{0}", 到底喝了多少瓶);
                Console.WriteLine("最后我还有几个瓶子:{0}", 喝酒.最后我还有几个瓶子());
                Console.WriteLine("最后我还有几个盖子:{0}", 喝酒.最后我还有几个盖子());
                Console.WriteLine("最后我还有好多钱:{0}", 喝酒.最后我还有好多钱());
                Console.WriteLine();
            }
            Console.ReadLine();
        }
    }

    public class 算喝了几瓶 {
        /// <summary>
        /// 多少钱
        /// </summary>
        private int 有好多钱 { get; set; }
        /// <summary>
        /// 多少盖子
        /// </summary>
        private int 有好多盖子 { get; set; }
        private int 有好多瓶子 { get; set; }
        private int 喝了几瓶 { get; set; }
        int 单价 = 2;
        int 盖子价 = 4;
        int 瓶子价钱 = 2;

        public 算喝了几瓶(int haoduoqian) {
            有好多钱 = haoduoqian;
            有好多盖子 = 0;
            有好多瓶子 = 0;
            喝了几瓶 = 0;
        }

        public void 不正常买酒喝酒() {
            if (能不能用钱买酒())
                用钱买酒();
            var rand = new Random();
            var next = rand.Next(0, 10);
            if (next % 2 == 0)
            {
                if (能不能用瓶子买酒())
                    用瓶子买酒();
                if (能不能用盖子买酒())
                    用盖子买酒();
            }
            else
            {
                if (能不能用盖子买酒())
                    用盖子买酒();
                if (能不能用瓶子买酒())
                    用瓶子买酒();
            }
            if (能不能用钱买酒() || 能不能用盖子买酒() || 能不能用瓶子买酒())
                不正常买酒喝酒();
        }

        public void 买酒喝酒() {
            if (能不能用钱买酒())
                用钱买酒();
            if (能不能用盖子买酒())
                用盖子买酒();
            if (能不能用瓶子买酒())
                用瓶子买酒();
            if (能不能用钱买酒() || 能不能用盖子买酒() || 能不能用瓶子买酒())
                买酒喝酒();
        }

        public int 到底喝了几瓶() {
            return 喝了几瓶;
        }

        private bool 能不能用瓶子买酒() {
            return 有好多瓶子 >= 瓶子价钱;
        }

        private bool 能不能用盖子买酒() {
            return 有好多盖子 >= 盖子价;
        }

        private bool 能不能用钱买酒() {
            return 有好多钱 >= 单价;
        }

        private void 用钱买酒() {
            int 买酒钱 = 有好多钱;
            int 买了几瓶酒 = 买酒钱 / 单价;
            有好多钱 = 买酒钱 % 单价;
            有好多瓶子 += 买了几瓶酒;
            有好多盖子 += 买了几瓶酒;
            喝了几瓶 += 买了几瓶酒;
        }

        private void 用盖子买酒() {
            int 换酒的盖子 = 有好多盖子;
            int 买了几瓶酒 = 换酒的盖子 / 盖子价;
            有好多盖子 = 换酒的盖子 % 盖子价;
            有好多瓶子 += 买了几瓶酒;
            有好多盖子 += 买了几瓶酒;
            喝了几瓶 += 买了几瓶酒;
        }

        private void 用瓶子买酒() {
            int 换就的瓶子 = 有好多瓶子;
            int 买了几瓶酒 = 换就的瓶子 / 瓶子价钱;
            有好多瓶子 = 换就的瓶子 % 瓶子价钱;
            有好多瓶子 += 买了几瓶酒;
            有好多盖子 += 买了几瓶酒;
            喝了几瓶 += 买了几瓶酒;
        }

        public int 最后我还有几个瓶子() {
            return 有好多瓶子;
        }

        public int 最后我还有几个盖子() {
            return 有好多盖子;
        }

        public int 最后我还有好多钱() {
            return 有好多钱;
        }

    }
}

新建一个控制台粘贴进去就能用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值