GESP2023年12月认证C++三级( 第三部分编程题(1、小猫分鱼))


🐱《小猫分鱼大冒险》


一、🏝️ 故事开始:海滩上的鱼山

在阳光灿烂的海边,有刚刚捕获的一大堆鱼 🐟🐟🐟。

突然,来了很多只小猫!🐱🐱🐱

它们决定按规则分鱼:


1、📜 分鱼规则

假设有 n 只小猫。

每一只小猫轮到时,都要这样做:

第一步:先看鱼堆

把鱼平均分成 n 份。

第二步:如果多出 i 条鱼

把多出的 i 条扔回大海 🌊

第三步:拿走其中一份

剩下的鱼留给下一只小猫。


2、🎯 任务目标

输入:

  • 小猫数量 n

  • 每次扔掉的鱼数 i

输出:

最少一开始需要多少条鱼,才能让所有小猫都成功分鱼!


3、📘 题目样例

输入:

复制代码
2
1

表示:

  • 一共有2只小猫

  • 每次都是,多1条鱼扔掉

输出:

复制代码
7

4、🧠 为什么答案是 7?


(1)🐱 第一只小猫来啦!

鱼有 7 条:

复制代码
🐟🐟🐟🐟🐟🐟🐟

分给 2 只猫:

复制代码
7 ÷ 2 = 3份,还多1条

把1条扔海里 🌊

剩下6条:

复制代码
3条 + 3条

第一只猫拿走3条。

剩下:

复制代码
3条

(2)🐱 第二只小猫来了!

3 条鱼分2份:

复制代码
3 ÷ 2 = 1份,多1条

扔掉1条,再拿走1条。

成功!🎉


(3)所以答案是:

复制代码
7


🧩 这道题目应该怎么做?


二、🌟 思路:使用枚举+模拟检查法:

从最小数字开始试:

复制代码
1,2,3,4,5,6,7...

谁第一次成功,谁就是答案!


1、🎯 判断一次是否成功的方法

假设当前有 fish 条鱼。

每只猫都检查:


条件1:能多出 i 条

也就是:

复制代码
fish % n == i

条件2:扔掉后还能平均分

剩下:

复制代码
fish - i

必须能被 n 整除。


条件3:猫拿走一份后更新鱼数

每份是:

复制代码
(fish - i) / n

猫拿走一份后还剩:

复制代码
fish = fish - i - 每份

2、✨ 举个例子(7条鱼)

复制代码
fish = 7
n = 2
i = 1

第一只猫:

复制代码
7 % 2 = 1 ✔

扔1条后剩6

每份3条

拿走3条

剩3条


第二只猫:

复制代码
3 % 2 = 1 ✔

成功!


3、💻 参考程序

复制代码
#include <iostream>
using namespace std;

int main()
{
    long long n, i;
    cin >> n >> i;

    long long fish = 1; // 从1开始试

    while(true)
    {
        long long temp = fish; // 临时鱼堆
        bool ok = true;

        // 每只猫都来试一次
        for(int cat = 1; cat <= n; cat++)
        {
            // 条件不满足
            if(temp % n != i || temp <= i)
            {
                ok = false;
                break;
            }

            long long one = (temp - i) / n; // 一份鱼
            temp = temp - i - one;          // 剩余鱼
        }

        if(ok)
        {
            cout << fish;
            break;
        }

        fish++;
    }

    return 0;
}

4、🪄 程序一步一步解释


第1步 读入数据

复制代码
cin >> n >> i;

输入:

复制代码
2 1

第2步 从1开始尝试

复制代码
fish = 1

如果失败:

复制代码
fish++

继续试!


第3步 每只猫来分鱼

复制代码
for(...)

让每只猫依次登场 🐱


第4步 找到答案就输出

复制代码
cout << fish;

5⏱️ 时间复杂度

假设答案是 X,小猫数是 n:

复制代码
O(X × n)

意思:

  • 试很多数字

  • 每个数字让 n 只猫检查


三、🌟 思路:逆推模拟法:

  • 前面写的是:从小到大试答案 → 这是 枚举 + 模拟检查

  • 我们现在:根据最后一轮反推上一轮鱼数,逐层构造答案逆推模拟法


1、🐱 这个方法更像"模拟法":

因为它不是盲目试:

复制代码
1,2,3,4,5...

而是先假设 最后一只猫分鱼时的状态,然后一步一步推回到第一只猫开始前的鱼数。

这相当于按规则"搭积木"。🧱


2、🧠 核心公式讲解


(1)🌟 最后一轮剩下的鱼

设最后一只猫分鱼前有:

复制代码
fish = k * n + i

意思:

  • 能分成 n

  • 多出 i 条鱼

  • k 表示每份有多少条


(2)🐱 这一轮分完后会剩多少?

扔掉 i 条后剩:

复制代码
k*n

每只猫拿走一份:

复制代码
k

剩下:

复制代码
k*n - k = k(n-1)

这就是"下一轮看到的鱼"。


(3)🌟 反推上一轮公式

如果当前这一轮开始前鱼数是 fish

那么上一轮开始前鱼数应满足:

复制代码
prev - i

分成 n 份,拿走一份后剩下当前 fish

整理后可得:

复制代码
prev = fish * n / (n - 1) + i

前提是:

复制代码
fish % (n - 1) == 0

否则不能整除,不合法。


3、💻 参考程序

复制代码
#include <iostream>
using namespace std;

int main()
{
    long long n, i;
    cin >> n >> i;

    long long k = 1;

    while (true)
    {
        bool ok = true;

        // 最后一只猫分鱼前的鱼数
        long long fish = k * n + i;

        // 反推前面 n-1 只猫
        for (int round = 1; round < n; round++)
        {
            if (fish % (n - 1) != 0)  //检查下是否整除,判断合理性。
            {
                ok = false;
                break;
            }

            fish = fish * n / (n - 1) + i;
        }

        if (ok)
        {
            cout << fish << endl;
            break;
        }

        k++;
    }

    return 0;
}

4、🪄 详细讲解


(1)🎯 输入

复制代码
2
1

表示:

  • 2只猫

  • 每次扔1条鱼


(2)🌟 第一次尝试:k=1

最后一只猫分鱼前:

复制代码
fish = 1*2+1 = 3

说明最后一只猫看到3条鱼。


往前推第一只猫开始前:

检查:

复制代码
3 % (2-1) = 0

成立。

反推:

复制代码
fish = 3 * 2 / 1 + 1 = 7

已经推了 n-1=1 次,结束。

答案就是:

复制代码
7

(3)🐱 三只猫例子

输入:

复制代码
3
1

k=1

最后一轮:

复制代码
fish = 1*3+1 = 4

往前推时可能失败。


k=3 时成功

最后一轮:

复制代码
fish = 3*3+1 = 10

往前推两次:

复制代码
10 -> 16 -> 25

所以答案:

复制代码
25

5、🌟 为什么这个方法更好?

相比从1开始枚举:

复制代码
1,2,3,4,5...

这个方法直接从合法末状态出发,速度更快!


四、📚 两种方法区别总结

方法 思路 特点
枚举检查法 从1开始试每个答案 直观,容易懂
反推模拟法 从最后一轮反推第一轮 更巧妙,更快

五、🎁 算法知识


1、🌟 模拟法

按照要求去操作,就是模拟法!

这题就是经典模拟题!


2、🌟 枚举法

一个一个试答案:

复制代码
1,2,3,4...

这叫枚举法!


3、🌟 记忆口诀

自己能模拟,

就让电脑模拟跑!
不会模拟,就从小开始找,

第一个成功就是宝!


相关推荐
不知名的老吴2 小时前
View的三大特性之一:迟绑定
开发语言·c++·算法
小雅痞2 小时前
[Java][Leetcode hard] 135. 分发糖果
java·算法·leetcode
黎阳之光2 小时前
黎阳之光:全域实景立体管控,重构智慧电厂与变电站数字孪生新范式
大数据·人工智能·算法·安全·数字孪生
嘻嘻哈哈樱桃2 小时前
数据流中的中位数 力扣--160
算法·leetcode·职场和发展
Huangjin007_2 小时前
【C++ STL篇(四)】一文拿捏vector常用接口!
开发语言·c++·学习
老约家的可汗2 小时前
深入浅出:Map与Set的核心原理与使用场景
数据结构·算法
草莓熊Lotso2 小时前
Linux 线程同步与互斥(一):彻底搞懂线程互斥原理、互斥量底层实现与 RAII 封装
linux·运维·服务器·开发语言·数据库·c++
j_xxx404_2 小时前
力扣算法题:字符串(最长公共前缀|最长回文子串)
c++·算法·leetcode
承渊政道2 小时前
【递归、搜索与回溯算法】(穷举vs暴搜vs深搜vs回溯vs剪枝:一文讲清概念与用法)
数据结构·c++·算法·决策树·深度优先·剪枝·宽度优先