P327. 渔夫捕鱼算法问题

问题描述:

A、B、C、D、E 这5个人合伙夜间捕鱼,凌晨时都已经疲惫不堪,于是各自在河边的树丛中找地方睡着了。第二天日上三竿时,A第一个醒来,他将鱼平分为5份,把多余的一条扔回河中,然后拿着自己的一份回家去了;B第二个醒来,但不知道A已经拿走了一份鱼,于是他将剩下的鱼平分为5份,扔掉多余的一条,然后只拿走了自己的一份;接着C、D、E依次醒来,也都按同样的办法分鱼。

问题:

问这5人至少合伙捕到多少条鱼?

样例

3121

这个问题可以通过逆向推理来解决。我们需要确定这5个人合伙捕到的最少鱼的数量。根据题目描述,每个人在分鱼时都将剩余的鱼平均分为5份,并将多余的鱼扔回河中。以下是解决问题的步骤:

解决思路

  1. 理解分鱼过程:

    • 当A醒来时,他将鱼分为5份,取走1份,多出的1条鱼被扔掉。
    • B醒来时,他在不知道A已经拿走1份的情况下,将剩余的鱼再分成5份,同样扔掉多出的1条。
    • C、D、E也按照同样的方法分鱼。
  2. 逆推每个人的操作:

    • 我们可以从最后一个人E开始推算,逐步计算回去,直到第一个人A。通过这种方法,我们可以求出最少的捕获数量。
  3. 设定变量:

    • 假设在每个人醒来时,剩下的鱼数量为 n。

C

#include <stdio.h>

int main() {
    int n = 1; // 从1条鱼开始
    while (1) {
        int fish = n; // 记录当前的鱼的数量

        // E 醒来
        fish = (fish - 1) * 4 / 5;
        if (fish < 0) break;

        // D 醒来
        fish = (fish - 1) * 4 / 5;
        if (fish < 0) break;

        // C 醒来
        fish = (fish - 1) * 4 / 5;
        if (fish < 0) break;

        // B 醒来
        fish = (fish - 1) * 4 / 5;
        if (fish < 0) break;

        // A 醒来
        fish = (fish - 1) * 4 / 5;
        if (fish < 0) break;

        // 如果最后的fish>=0,更新n并继续查找
        n++;
    }
    printf("%d\n", n - 1); // 输出最后的最小鱼的数量
    return 0;
}
#include <stdio.h>

int main() {
    int nE = 0; // E 醒来时的鱼数量
    int nA = 0; // A 醒来时的鱼数量
    int k = 0;  // k 是某个整数,用于表示 E 的情况

    // 从最小的 nE 开始,直到找到合适的 nA
    while (1) {
        nE = 5 * k + 1; // E 的鱼数量公式
        int nD = nE + 1; // D 醒来时的鱼数量
        int nC = nD + 1; // C 醒来时的鱼数量
        int nB = nC + 1; // B 醒来时的鱼数量
        nA = nB + 1;     // A 醒来时的鱼数量

        // 检查每个阶段是否符合条件
        if ((nE - 1) % 5 == 0 && (nD - 1) % 5 == 0 && (nC - 1) % 5 == 0 && (nB - 1) % 5 == 0) {
            // 如果条件满足,打印 A 醒来时的鱼数量
            printf("%d\n", nA);
            break; // 找到合适的数量后退出循环
        }

        k++; // 增加 k,尝试下一个可能的数量
    }

    return 0;
}

C++

#include <iostream>
using namespace std;

int main() {
    int n = 1; // 从1条鱼开始
    while (true) {
        int fish = n; // 记录当前的鱼的数量

        // E wakes up
        fish = (fish - 1) * 4 / 5;
        if (fish < 0) break;

        // D wakes up
        fish = (fish - 1) * 4 / 5;
        if (fish < 0) break;

        // C wakes up
        fish = (fish - 1) * 4 / 5;
        if (fish < 0) break;

        // B wakes up
        fish = (fish - 1) * 4 / 5;
        if (fish < 0) break;

        // A wakes up
        fish = (fish - 1) * 4 / 5;
        if (fish < 0) break;

        // 如果最后的fish>=0,更新n并继续查找
        n++;
    }
    cout << n - 1 << endl; // 输出最后的最小鱼的数量
    return 0;
}

上面三个代码都会超时 下面是优化的代码

尝试从初始的鱼的数量开始逐步检查每个渔夫醒来后的鱼的数量。虽然这个方法能得出答案,但由于检查的范围太大,因此可能会导致超时

优化思路

扫描二维码关注公众号,回复: 17481852 查看本文章
C语言
#include <stdio.h>

int main() {
    int n = 1; // 从1条鱼开始
    while (1) {
        int fish = n; // 记录当前的鱼的数量
        int valid = 1; // 标记当前的鱼数是否有效

        // 从 A 到 E 依次推导
        for (int i = 0; i < 5; ++i) {
            if ((fish - 1) % 5 != 0) { // 如果不能被平分
                valid = 0; // 该情况不满足
                break;
            }
            fish = (fish - 1) * 4 / 5; // 计算下一个渔夫的鱼数
        }

        if (valid) {
            printf("%d\n", n); // 找到的最小数量
            break;
        }
        n++; // 继续尝试下一个数量
    }
    return 0;
}

C++
#include <iostream>
using namespace std;

int main() {
    int n = 1; // 从1条鱼开始
    while (true) {
        int fish = n; // 记录当前的鱼的数量
        bool valid = true; // 标记当前的鱼数是否有效

        // 从 A 到 E 依次推导
        for (int i = 0; i < 5; ++i) {
            if ((fish - 1) % 5 != 0) { // 如果不能被平分
                valid = false; // 该情况不满足
                break;
            }
            fish = (fish - 1) * 4 / 5; // 计算下一个渔夫的鱼数
        }

        if (valid) {
            cout << n << endl; // 找到的最小数量
            break;
        }
        n++; // 继续尝试下一个数量
    }
    return 0;
}
 C语言版本
#include <stdio.h> // 引入标准输入输出库

int main() {
    int x = 0; // 计数器,用于控制循环次数,最大值为6
    double y = 6, m = 6; // y 初始化为6,m 也初始化为6,y 表示鱼的数量,m 用于存储当前整数值

    // 进入一个无限循环,直到 x 达到6为止
    while (x < 6) {
        // 根据公式 y = y * 1.25 + 1 计算下一个鱼的数量
        // 这个公式是为了模拟渔夫们分鱼后的数量变化
        y = y * 1.25 + 1; 

        // 检查 y 是否为整数,如果不是,则 y 的值大于其整数部分
        if (y > (int)y) { 
            // 如果 y 不是整数,则增加 m 的值
            m++; 
            // 将 y 设置为 m 的值,使其为下一个整数
            y = m; 
            // 重置 x 为0,重新开始计数
            x = 0; 
        }

        // 增加计数器 x,以便跟踪循环次数
        x++; 
    }

    // 打印 y 的值,使用 %.0lf 格式确保输出为整数
    printf("%.0lf", y); 

    return 0; // 返回0表示程序成功结束
}

正推法

正推是从一个最小的可能值(这里是1条鱼)开始,依次进行计算,直到满足所有渔夫的分鱼条件。

代码解析

  1. 初始化:

    • fish = 1:从1条鱼开始尝试。
  2. 循环:

    • while True:不断尝试增加鱼的数量,直到找到满足条件的数量。
    • total_fish = fish:记录当前的鱼的数量。
  3. 检查条件:

    • 使用一个for循环来模拟5个渔夫的分鱼过程。
    • 在每次循环中:
      • 检查(total_fish - 1) % 5 == 0:这确保了当前剩余鱼数减去1后能被5整除,符合渔夫的分鱼逻辑。
      • 如果条件满足,计算下一个渔夫醒来后的鱼的数量:total_fish = (total_fish - 1) // 5 * 4
      • 如果条件不满足,设置valid = False并跳出循环。
  4. 找到结果:

    • 如果valid保持为True,则返回当前的fish值。
    • 如果不满足条件,则增加鱼的数量并重新进行检查。

正推 vs 反推

  • 正推:从较小的可能数量开始,逐步向上推导,直到找到符合条件的最小数量。
  • 反推:从最终状态出发,逐步推导回去,通常是通过假设每个渔夫醒来时的鱼数和他们的操作来确定初始的鱼数。

在这个问题中,你的代码通过正推方法成功找到满足条件的鱼的数量。

python

def find_minimum_fish():
    fish = 1  # 初始化鱼的数量,从1条鱼开始尝试
    while True:  # 无限循环,直到找到满足条件的鱼的数量
        total_fish = fish  # 将当前鱼的数量赋值给 total_fish,方便后续计算
        valid = True  # 用于标记当前数量是否满足所有渔夫的分鱼条件
        
        # 依次验证5个渔夫
        for _ in range(5):  # 遍历5个渔夫
            # 检查当前鱼的数量减去1是否能被5整除
            if (total_fish - 1) % 5 == 0:  
                # 如果可以被5整除,计算下一个渔夫醒来后剩下的鱼的数量
                total_fish = (total_fish - 1) // 5 * 4  # 先减去1条鱼,然后将剩下的鱼数的四分之五赋值给 total_fish
            else:
                valid = False  # 如果不符合条件,设置标记为 False
                break  # 跳出循环,不再检查后续的渔夫
        
        # 如果 valid 仍然为 True,说明所有渔夫的条件都满足
        if valid:
            return fish  # 返回当前鱼的数量,作为结果
        
        fish += 1  # 如果当前数量不满足条件,增加鱼的数量并重新开始验证

# 运行结果
result = find_minimum_fish()  # 调用函数并获取结果
print(result)  # 打印结果

 C语言

#include <stdio.h>

int find_minimum_fish() {
    int fish = 1; // 从1条鱼开始

    while (1) {
        int total_fish = fish; // 记录当前的鱼的数量
        int valid = 1; // 用于判断是否符合所有渔夫的要求

        // 依次验证5个渔夫
        for (int i = 0; i < 5; ++i) {
            if ((total_fish - 1) % 5 == 0) { // 检查能否被5整除
                total_fish = (total_fish - 1) * 4 / 5; // 计算下一个渔夫的鱼的数量
            } else {
                valid = 0; // 不符合条件
                break; // 跳出循环
            }
        }

        if (valid) {
            return fish; // 找到符合条件的鱼的数量
        }
        fish++; // 增加鱼的数量并重新检查
    }
}

int main() {
    int result = find_minimum_fish(); // 调用函数
    printf("%d\n", result); // 输出结果
    return 0; // 程序正常结束
}

C++

#include <iostream>
using namespace std;

int find_minimum_fish() {
    int fish = 1; // 从1条鱼开始

    while (true) {
        int total_fish = fish; // 记录当前的鱼的数量
        bool valid = true; // 用于判断是否符合所有渔夫的要求

        // 依次验证5个渔夫
        for (int i = 0; i < 5; ++i) {
            if ((total_fish - 1) % 5 == 0) { // 检查能否被5整除
                total_fish = (total_fish - 1) * 4 / 5; // 计算下一个渔夫的鱼的数量
            } else {
                valid = false; // 不符合条件
                break; // 跳出循环
            }
        }

        if (valid) {
            return fish; // 找到符合条件的鱼的数量
        }
        fish++; // 增加鱼的数量并重新检查
    }
}

int main() {
    int result = find_minimum_fish(); // 调用函数
    cout << result << endl; // 输出结果
    return 0; // 程序正常结束
}

猜你喜欢

转载自blog.csdn.net/qq_73454087/article/details/142906494