分鱼问题—总结
问题描述
A、B、C、D、E这5个人合伙夜间捕鱼,凌晨时都已经疲惫不堪,于是各自在河边的树丛中找地方睡着了。第二天日上三竿时,A第一个醒来,他将鱼平分为5份,把多余的一条扔回河中,然后拿着自己的一份回家去了;B第二个醒来,但不知道A已经拿走了一份鱼,于是他将剩下的鱼平分为5份,扔掉多余的一条,然后只拿走了自己的一份;接着C、D、E依次醒来,也都按同样的办法分鱼。问这5人至少合伙捕到多少条鱼?每个人醒来后所看到的鱼是多少条?
问题分析
假设5个人合伙捕了x条鱼,则“A第一个醒来,他将鱼平分为5份,把多余的一条扔回河中,然后拿着自己的一份回家去了”之后,还剩下4(x-1)/5条鱼。
这里实际包含了一个隐含条件:假设Xn为第n(n=1、2、3、4、5)个人分鱼前鱼的总数,则(Xn-1)/5必须为正整数,否则不合题意。(Xn-1)/5为正整数即(X〜l)mod5=0必须成立。
又根据题意,应该有下面等式:
X4=4(X5-1)/5
X3=4(X4-1)/5
X2-4(X3-1)/5
X1=4(X2-1)/5则一旦给定X5,就可以依次推算出X4、X3、X2和X1的值。要保证X5、X4、X3、X2和X1都满足条件(Xn-1)mod5=0,此时的X5则为5个人合伙捕到的鱼的总条数。题目中强调了 “至少”合伙捕到的鱼,此时题目的答案唯一。该问题可使用多种求解。我认为递归是最难懂的一个解法。
方法一和方法二为穷举法,方法三为递归法。
方法一
思路:从5开始,尝试每一个数字(这个数字是鱼最初的数量),分别计算出后四个人见到的鱼的数量记为 S_D、S_C、S_B、S_A,之后判断 S_D、S_C、S_B、S_A是否能被5 除后余1
#include "stdio.h" #include "stdlib.h" #include "string.h" int main() { int sum, S_A, S_B, S_C, S_D, S_E; // S_i 这个人醒来看到的鱼 int i =5; while (1) { // printf("------"); // i 是E拿走的 S_E = 5*i + 1; S_D = 5*S_E/4 + 1; S_C = 5*S_D/4 + 1; S_B = 5*S_C/4 + 1; S_A = 5*S_B/4 + 1; if(((S_E-1) % 5 == 0) &&((S_D-1) % 5 == 0) &&((S_C-1) % 5 == 0) &&((S_B-1) % 5 == 0) &&((S_A-1) % 5 == 0)) { printf("S_A:%d\n", S_A); break; } i++; // printf("E看到的总数%d\n", 5*i + 1); // printf("E带走的是%d\n", i); // return 5*i + 1; //. 返回E看到的总数,即D留下的 } printf("OK\n"); printf("分别见到鱼的条数:\n第一个人:%d \n第二个人:%d \n第三个人:%d \n第四个人:%d\n第五个人:%d \n", S_A, S_B, S_C, S_D, S_E); }
方法二
从5开始,尝试每一个数字(这个数字是最后一个人见到鱼的数量),分别计算出前四个人见到的鱼的数量记为 j、k、l、m,之后判断 j-1、k-1、l-1、m-1是否能被5 除后余1
// 就不代码实现了,大致如下,其他同上
S_A=5*S_B/4+1;
<a href="https://blog.csdn.net/xinanbo/article/details/113817394">点击</a><br></hr>
方法三:递归完成
在main()函数中构建一个不定次数的do-while循环。定义变量x表示5个人合伙可能捕到的鱼的条数,可以取x的最小值为6,让x值逐渐增加,x每一次取值,都增加5,直到找到一个符合问题要求的答案。由于题目中问“这5人至少合伙捕到多少条鱼”,而我找到的第一个x值就是5个人至少捕到的鱼的总条数。
通过这个循环,就可以对每一个的可能情况进行检查。当然,是通过调用分鱼的递归函数来进行检查的。
分鱼的递归函数如下:
fish()函数中包含了两个参数:n和x。n表示参与分鱼的人数,x表示n个人分鱼前鱼的总条数。这两个参数都是由main()函数中传递进来的。
根据前面的分析,当n=5时,(x-1)mod5==0必须成立,否则该x值不是满足题意的值,退出fish()函数,返回到main()函数,main()函数中再传递新的x值到fish中进行检验。如果(x-1)mod5==0条件成立,则要判断n=4时,(x-1)mod5=0条件是否成立,需要注意的是,此时的形参x是4个人分鱼前鱼的总条数,即f(5,x)递归调用f(4,(x-1)/5*4)。这样依次进行下去,直到n=1时,(x-1)mod5==0条件仍成立,则说明开始从main()函数中传递进来的x值是符合题意要求的一个值,可以逐层从递归函数中返回,每次返回值都为1,直至返回到main()函数。
#include<stdio.h> /*分鱼递归函数*/ int fish(int n, int x) { if((x-1)%5 == 0) { if(n == 1) return 1; /*递归出口*/ else return fish(n-1, (x-1)/5*4); /*递归调用*/ } return 0; /*x不是符合题意的解,返回0*/ } int main() { int i=0, flag=0, x; do { i=i+1; x=i*5+1; /*x最小值为6,以后每次增加5*/ if(fish(5, x)) /*将x传入分鱼递归函数进行检验*/ { flag=1; /*找到第一个符合题意的x则置标志位为1*/ printf("五个人合伙捕到的鱼总数为%d\n", x); } } while(!flag); /*未找到符合题意的x,继续循环,否则退出循环*/ return 0; }
<a href="http://c.biancheng.net/cpp/html/3378.html">点击</a><br></hr>
看到一位大佬写的python代码,标注很详细 :
# 初次见面,随便假设一个鱼的总数,不要设置太高,以免错过答案 fish = 10 # 这里使用while循环,如果不满足 在依据新假设的鱼总数,再进循环(咱肯定不能总是自己设置一个鱼的数量) while True: # 每次进入while循环,我们需要先暂存一下fish的值,方便在循环里面使用,避免覆盖fish的值 tmp_fish = fish # 每次进入while循环,我们都假设这个数是可以满足条件的 enough = True # 开始每次的for循环(每次for循环的任务就是拿我们假设的值 给5个人分以下,看能不能满足要求 # 如果不满足要求,就把enough设置为False,然后跳出for循环 # 如果满足要求,enough的值会继续保持为True,for循环走完后,就会跳出while循环,打印结果(也可以直接在while里面打印结果) for _ in range(5): # 根据要求,鱼必须丢掉一个 并整除5 才能满足要求 if (tmp_fish - 1) % 5 == 0: # 当满足要求时,我们需要计算出下一个人开始分鱼时,鱼的总舒缓亮 tmp_fish = ( (tmp_fish - 1) / 5 ) * 4 else: # 如果5次循环,有一次进入这个 ,就表示不满足要求 enough = False break if enough: # 表示假设的值满足要求,所以我们跳出while循环,去打印结果 break else: # 表示假设的值不满足要求,所以我们把假设的值加1,再次循环 fish += 1 print("鱼的条数:" + str(fish))
<a href="
本文来自博客园,转载请注明原文链接:https://www.cnblogs.com/WG11/p/fish.html,作者:def_Class