通俗易懂、简单粗暴得解决猴子分桃问题

起因

海滩上有一堆桃子,五只猴子来分。第一只猴子把这堆桃子分为五份,多了一个,这只猴子把多的一个仍入海中,拿走了一份。第二只猴子把剩下的桃子平均分成五份,又多了一个,它同样把多的一个扔入海中,拿走了一份,第三、四、五只猴子都是这样做的,问海滩上原来最少有多少个桃子?

起初我看到这道题是崩溃的,咋又碰到数学题啦!我数学向来差的一批,所以这道题我是真真的没想出啥解决的方法,于是乎我便到网上查找解题方案,好家伙,不查不要紧,一查我更崩溃了,因为我发现那些解释我一个都没懂!尽管N多个人从N多个方向提出了解题思路,但我是真弄没弄清楚他们列出来的数学公式。连解题思路我都没懂,就更别谈网上的代码了,代码复制下来是能跑出正确结果,但是从开头的数值初始化,到最后的打印语句,我没有一行能理解的o(╥﹏╥)o

就在我万念俱灰、瞎搞乱搞之际,突然一条思路在我脑袋里冒出,顺着这条思路我瞬间理清了解决方法,并相当轻松的完成了代码!!(不要问我思路咋突然就理清了,我也母鸡啊╮(╯▽╰)╭)这个思路不需要你有啥数学知识,也不用列什么数学公式,只有非常简单的一点逻辑分析,我相信在这个思路下,大家就算数学糊烂到爆,也能理解这道题,毕竟数学再烂能有我烂!?

问题分析

分析这个猴子偷桃的问题之前,可以参考一下我之前写的这篇,暴力解三元一次方程的思路:用for循环解三元一次方程的思路

拥有好的数学功底,固然能更好的写出程序,但是程序不仅仅只有数学,还有逻辑分析。数学解不出来,我就用程序暴力解决呗,程序不就是代替我们人类做起来重复又繁琐的事嘛。回到猴子分桃的问题上来, 目前咱们从题目中得到的信息说多不多,说少不少,咱们一步一步得来提取:

首先,有一个固定的数值是显然易见的,那么就是猴子有5只,他们一共分了5次桃。那咱们就将猴子的变量先提取出来,monkey = 5呗。

然后,每次分桃时候的桃子数量有5份+1,至于每份是多少,不知道。最难的地方就在这,每份的桃子数不光不确定,而且每次分完桃子后,桃子的数量还会变化!!不过别急着用数学的公式来解决他,这里虽然是最难搞定的,但也是突破口。

咱继续进一步思考,猴子每次会分5份桃子+1个,然后丢掉一个,拿走一其中份,这不就代表着 将 桃子数先 - 1,然后再拿走五分之一数量的桃子,只剩下五分之四的桃子,并且下一次分桃又重复着这种操作,总共进行了5次。 (不要嫌我啰嗦反复讲这些已知的,代码马上就可以写出了)。同样的操作,进行了5次,嗯哼,大家有联想到程序里的什么吗!? 没错,这不就是循环嘛!! 一共循环了5次呗。OK,咱们现在知道要弄一个循环出来了,循环的执行代码块又是啥咧?

int i = 1;// 循环的条件变量
while(i <= 5) {
    //这里要做点啥咧......
    i++;
}

有一个数,要 - 1后 再 剩下 五分之四,并且要不断的进行五次,也就是说,咱们要求的这个数要达到这些条件才符合我们的要求。符合要求,嗯哼?大家又联想到程序里的什么吗?是滴,就是判断嘛,一个数要符合这个条件并持续五次,才算达到要求。OK,那不就代表咱们要把判断条件放到那个五次循环里进行嘛,循环执行完毕后都通过了判断,就代表找到那个数了!

咱们要最终找到的那个数是桃子,所以变量为了见名知意,就定为peach哈

int i = 1;
while(i <= 5) {
    // -1后才能分平均五份,不就代表着模5要等于1嘛
    if (peach % 5 == 1) {
        // 每次弄完后,就剩下原数量的 - 1后的五分之四了,所以每次要将这个数更新一下
        // peach / 5就代表现在的每份桃子的数量
        // 拿掉一份后,就是只剩下四份,所以才乘以4
        // 有人问不是多了一个桃子嘛,怎么不用-1呢,因为程序/5会直接减掉余数,所以不用-1后再除。
        // 如果想-1后再除以5,也是一样的,只是没必要
        peach = (peach / 5) * 4;
    }
    i++;
}

非常好,感觉有点意思了。现在下一个问题自然就摆在我们眼前,peach我该定义成啥数啊?呃...要定义啥数我也不知道,要是我知道的话那就不用求这个数了,答案不就出来了嘛。 别急,不知道啥数,咱们就找嘛,我们一个数字一个数字的去试,看看到底哪个数符合这个条件,答案不就出来了? 一个一个数字的去试,咱们人做起来肯定要疯,不过聪明的你肯定想到了,用程序去做呗!注意哦,这里已经将问题的解决思路全部说出来了,就是 一个数一个数的去尝试,如果符合题意条件的就代表那个数找到了! 咱们现在要做的就是,怎样去一个数一个数的去找:

int i = 1; 
int peach = 1; // 要寻找的数,咱们从最小的整数开始往下一个一个的找
while(i <= 5) {
    if (peach % 5 == 1) {
        peach = (peach / 5) * 4;
    }
    i++;
    peach++; // 每循环一次peach的数量就加一次,然后再进行尝试判断
}

写到这里有一个程序的问题,就是循环只有五次,peach的数量只能加五次,这五次要是没符合,程序还是会结束,所以这里就要进行逻辑上的优化,以达到我能一个数一个数进行尝试的要求(这一步和题目其实没啥关系,就是程序逻辑的优化):

int i = 1; 
int peach = 1; // 要寻找的数,咱们从最小的整数开始往下一个一个的找
while(i <= 5) {
    if (peach % 5 == 1) {
        peach = (peach / 5) * 4;
        i++;// 只有数值判断成功了,才会将循环条件更新
    } else { // 如果数值没有判断成功,就代表数值不符合要求,就要继续找下一个数
        peach++; // 每循环一次peach的数量就加一次,然后再进行尝试判断
    }
}

为了讲解的更清楚,这一步我没有完全优化好,就是说这块代码还是有很多问题。比如,要是一个数值只要符合 % 5 == 1了,就会进行代码块里的数值更新(peach呀,循环条件i呀都会变化掉),但是我们要求的是得连续符合5次才行,现在一次符合就将我们数值给变化了,那可不行,所以咱进行下一步优化,一定要保证,如果不是连续符合五次的话,我的其他数值就不能变化,然后重新再来,进行下一个数值的判断

int i = 1; 
int peach = 1;
int count = 1; // 因为每次分桃的数量会更新,所以必须额外声明一个变量来不断递增做实验
while(i <= 5) {
    if (peach % 5 == 1) {
        peach = (peach / 5) * 4;
        i++;
    } else { 
         // 每循环一次peach的数量就加一次,然后再进行尝试判断,为了避免peach在if代码块里变化过,所以额外的一个变量来做更新
        count++;
        peach = count;
        i = 1; // 为了避免循环条件变量i也被更新过,所以只要有一次判断不成功就重新来过,重新设为初始值
    }
}

至此代码就已经完全实现了,为了更加的见名知意,我们将循环条件变量i改一下名,既然是五只猴子来分,就将i改成monkey,以下是完整代码,我会将所有注释都加上,好方便理解(如果看完整代码对其中某一步不明白,就看一下之前我是怎样一步一步增加代码的):

/**
 * 
 * @function 猴子分桃
 * @author RudeCrab
 * @date 2019年4月1日上午8:30:21
 * @version 1.0.0
 * @copyright RudeCrab
 */
public class Peach {
    public static void main(String[] args) {
        int monkey = 1;// 要分桃(判断)的次数,就是到第几个猴子分桃了
        int peach = 1;// 每次分桃(判断)的总数,用来判断是否符合条件
        int count = 1;// 因为每次分桃(判断)后数量会更新,所以必须额外声明一个变量来递增做实验
        // 循环进行分桃判断
        while (monkey <= 5) {
            // 怎样才算分桃(判断)成功呢,就是要%5后还剩一个,然后来五次
            // -1后才能分平均五份,不就代表着模5要等于1嘛
            if (peach % 5 == 1) {
                // 每次弄完后,就剩下原数量的 - 1后的五分之四了,所以每次要将这个数更新一下
                // peach / 5 就代表现在的每份桃子的数量
                // 拿掉一份后,就是只剩下四份,所以才乘以4
                // 有人问不是多了一个桃子嘛,怎么不用-1呢,因为程序/5会直接减掉余数,所以不用-1后再除。
                // 如果想-1后再除以5,也是一样的,只是没必要
                peach = (peach / 5) * 4;
                // 分桃成功的话,就将循环条件变量monkey进行更新
                monkey++;
            } else { // 如果数值没有判断成功,就代表数值不符合要求,就要继续找下一个数
                // 代表刚才那个数字不能达到五次的要求,所以重新再来
                
                count++; // 为了避免peach在if代码块里变化过,所以额外的一个变量来做更新
                peach = count; // 然后peach重新被赋值,这样peach就算判断失败了,也不会受影响
                monkey = 1; // 为了避免循环条件变量也被更新过,所以只要有一次判断不成功就重新来过,重新设为初始值
            }
        }
        // 最后整个循环执行完毕了,就代表数值找到了,打印数值即可
        // 这里为啥不打印peach呢,因为peach在代码块里每次分桃(判断)后都被更新了,所以count才是真实数值
        System.out.println("桃的最小总数为:" + count);
    }
}

最后输出结果为3121,完美求出结果。这个解题思路说白了就是暴力求解,一个数一个数的去尝试判断,哪个数能符合题意的就代表数值找到了。 是不是用不到难以理解的数学公式就搞定了,嘿嘿。其实代码量非常少,根本没多少行,但是为了方便理解,所以我的注释尽量加的足够多,大家要是看着一堆注释要点晕,可以先将代码复制下来,然后将注释单独保存,将代码和注释对照查看肯定能够更好的理解!

到这里大家以为就结束了嘛,nonono,要是这个思路只能解决这一个问题,这顶多叫这一题的特定方法,既然我说了是一种解题的思路,就代表这一种类型的问题都可以搞定,那么接下来让我们看一下另一个 猴子分桃问题。

扩展练习

有一只猴子,摘了很多桃子,他每天吃掉一些,剩下的桃子比之前的全部的一半少一个。他吃了十天才吃完,请问他一共摘了多少桃子?

看到这个问题是不是觉得不难,按照之前的方法去解就OK了咯,这和上一题不同点就只是在于数值判断的条件循环的次数。 我就不一步一步分解这一题了,直接贴上所有代码和注释,如果期间有不明白的,回到文章前面看一下思路就能搞定:

public static void showPeach2() {
    int day = 1; // 天数,即循环的判断条件
    int peach = 1; // 桃子数,即要求出的值
    int count = 1; // 额外的变量,用来保持桃子数量的更新
    while (day <= 10) { // 这里改成10次循环
        if (peach % 2 == 0 && peach > 0) { // 因为0%2也会等于0,所以得加一个限定条件数量要大于0才行
            peach = peach / 2 - 1; // 更新变量
            day++; // 更新循环条件
        } else { // 如果没有判定成功,代表重新来过
            count++;
            peach = count;
            day = 1;
        }
    }

    System.out.println("总桃子为:" + count);
    /*输出结果为2046*/
}

总结和思考

和解三元一次方程一样,数学咱不会解,就用程序来暴力解,毕竟程序可以替人类做那些重复繁琐的事嘛,这也是程序的用途所在!

如果对于这题还有地方不明白,请留言,咱们一起交流

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,014评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,796评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,484评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,830评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,946评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,114评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,182评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,927评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,369评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,678评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,832评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,533评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,166评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,885评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,128评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,659评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,738评论 2 351

推荐阅读更多精彩内容