这道题目源自信息学奥赛GESP三级考试,题目名为"小猫分鱼"。这是一个经典的数学逻辑问题,考察选手对循环结构和数学建模的能力。题目描述如下:
有n只小猫一起捕鱼,捕完鱼后它们决定将鱼分成n份,每只小猫拿一份。分完后还剩i条鱼。然后下一只小猫将剩下的鱼再次分成n份,同样剩i条。如此重复直到所有小猫都分过鱼。问最初至少有多少条鱼?
这个问题看似简单,但蕴含着巧妙的数学思维。我们需要找到一个最小的初始鱼量s,使得经过n次分配后,每次分配都满足"分成n份剩i条"的条件。
这个问题本质上是一个递推问题。我们可以从最后一次分配倒推:
关键在于每次分配后剩余的鱼必须能被(n-1)整除,这样才能保证下一次分配时鱼的数量是整数。
由于直接求解数学公式比较复杂,题目采用了暴力枚举的方法:
这种方法虽然时间复杂度较高,但对于题目给定的数据范围是可行的。
cpp复制#include<bits/stdc++.h>
using namespace std;
int main() {
long long n, i, s;
bool f;
cin >> n >> i;
for (long long k = 1; ; k++) {
f = true;
s = k * n + i;
for (long long j = 1; j <= n - 1; j++) {
if (s % (n - 1) != 0) {
f = false;
break;
}
s = s * n / (n - 1) + i;
}
if (f == true) break;
}
cout << s << endl;
return 0;
}
n: 小猫的数量i: 每次分配后剩余的鱼数s: 当前假设的初始鱼量f: 标志变量,表示当前k值是否满足条件k: 枚举的变量,用于计算初始鱼量外层循环for (long long k = 1; ; k++)是一个无限循环,通过break语句退出。它枚举可能的k值,从1开始逐步增加。
内层循环for (long long j = 1; j <= n - 1; j++)模拟n-1次分配过程:
当内层循环完整执行n-1次且每次都满足条件时,f保持为true,此时跳出外层循环并输出结果。
这个问题实际上可以通过数学方法直接求解。设最初有x条鱼,根据题意可以建立方程:
x ≡ i mod n
x * (n/(n-1)) ≡ i mod n
...
通过解这个同余方程组,可以找到最小的x。这种方法的时间复杂度是O(1),远优于暴力枚举。
即使采用暴力枚举,也可以进行一些优化:
由于鱼的数量可能很大,必须使用long long类型而非int,否则会导致计算结果错误。
如果输入的n为1,程序会陷入无限循环,因为n-1=0会导致除零错误。实际题目中n应该大于1。
验证代码正确性的几个测试用例:
对于编程初学者,建议按照以下步骤理解这个问题:
这道题目很好地训练了以下几个方面的能力:
在实际教学中,可以引导学生先思考数学解法,再考虑编程实现,最后探讨优化方案。这样的训练对培养计算思维非常有帮助。