问题背景:百钱买百鸡
这是一个中国古代的数学问题,出自《孙子算经》。

(图片来源网络,侵删)
问题描述: 公鸡5钱一只,母鸡3钱一只,小鸡1钱三只,用100钱买100只鸡,问公鸡、母鸡、小鸡各应买多少只? 分析:**
-
变量定义:
- 设公鸡的数量为
cock。 - 设母鸡的数量为
hen。 - 设小鸡的数量为
chick。
- 设公鸡的数量为
-
建立方程:
- 数量方程: 公鸡、母鸡、小鸡的总数是100只。
cock + hen + chick = 100 - 金额方程: 公鸡、母鸡、小鸡的总价是100钱。
5 * cock + 3 * hen + (chick / 3.0) = 100
- 数量方程: 公鸡、母鸡、小鸡的总数是100只。
-
约束条件:
(图片来源网络,侵删)cock,hen,chick都必须是非负整数。- 小鸡的数量
chick必须是3的倍数,因为“1钱三只”,chick % 3 == 0。
解题思路
暴力枚举法(最直观)
这是最简单、最容易理解的思路,我们遍历所有可能的公鸡和母鸡的数量,然后计算出小鸡的数量,最后检查是否满足所有条件。
-
确定循环范围:
- 公鸡最贵(5钱),100钱最多买
100 / 5 = 20只。cock的范围是0到20。 - 母鸡次贵(3钱),最多买
100 / 3 = 33只。hen的范围是0到33。 - 小鸡的数量由
cock和hen决定,chick = 100 - cock - hen。
- 公鸡最贵(5钱),100钱最多买
-
判断条件:
- 在循环内部,我们计算
chick。 - 然后检查
chick是否是3的倍数(chick % 3 == 0)。 - 并且检查总金额是否等于100(
5*cock + 3*hen + chick/3 == 100),为了防止整数除法带来的误差,我们可以使用chick/3.0或者(5*cock + 3*hen)*3 + chick == 300来进行精确判断。
- 在循环内部,我们计算
C语言代码实现(暴力枚举法):
#include <stdio.h>
int main() {
int cock, hen, chick;
int found = 0; // 用于标记是否找到解
printf("百钱买百鸡问题的所有可能解:\n");
printf("--------------------------------\n");
printf("公鸡\t母鸡\t小鸡\n");
printf("--------------------------------\n");
// 遍历公鸡的数量 (0-20)
for (cock = 0; cock <= 20; cock++) {
// 遍历母鸡的数量 (0-33)
for (hen = 0; hen <= 33; hen++) {
// 计算小鸡的数量
chick = 100 - cock - hen;
// 判断条件:小鸡数量必须为非负整数且是3的倍数,且总价为100
if (chick >= 0 && chick % 3 == 0 && (5 * cock + 3 * hen + chick / 3) == 100) {
printf("%d\t%d\t%d\n", cock, hen, chick);
found = 1;
}
}
}
if (!found) {
printf("没有找到符合条件的购买方案,\n");
}
return 0;
}
代码解析:
- 我们使用两个嵌套的
for循环来枚举cock和hen的所有可能组合。 chick = 100 - cock - hen直接计算出小鸡的数量,这保证了“百鸡”的条件。if语句是关键,它检查三个条件:chick >= 0:确保小鸡数量不是负数。chick % 3 == 0:确保小鸡数量是3的倍数。(5 * cock + 3 * hen + chick / 3) == 100:确保总价是100钱,这里chick / 3是整数除法,但因为前面已经保证了chick是3的倍数,所以结果是准确的。
- 如果
found变量没有被置为1,说明没有找到解。
优化枚举法
我们可以稍微优化一下,减少不必要的计算,因为小鸡的数量 chick 必须是3的倍数,cock + hen 也必须满足 (100 - cock - hen) 是3的倍数,即 cock + hen 和 100 对3的余数相同。
这个优化对性能提升不大,但对于理解数学关系很有帮助,对于这种小规模问题,暴力枚举法已经足够高效和清晰。
数学推导法(更高效)
我们可以用数学方法减少一个循环。
- 从数量方程
chick = 100 - cock - hen出发。 - 将其代入金额方程:
5*cock + 3*hen + (100 - cock - hen) / 3.0 = 100 - 为了消去分母,方程两边同时乘以3:
15*cock + 9*hen + 100 - cock - hen = 300 - 合并同类项:
14*cock + 8*hen = 200 - 两边同时除以2,简化方程:
7*cock + 4*hen = 100
我们只需要一个循环来遍历 cock,然后通过方程 hen = (100 - 7*cock) / 4 直接计算出 hen 的值,再检查是否为整数即可。
C语言代码实现(数学推导法):
#include <stdio.h>
int main() {
int cock, hen, chick;
printf("百钱买百鸡问题的所有可能解(数学推导法):\n");
printf("--------------------------------\n");
printf("公鸡\t母鸡\t小鸡\n");
printf("--------------------------------\n");
// 遍历公鸡的数量
// 由于 7*cock <= 100, cock <= 14.28, 所以cock的范围是0到14
for (cock = 0; cock <= 14; cock++) {
// 根据推导出的公式计算母鸡的数量
// (100 - 7*cock) 必须能被4整除
if ((100 - 7 * cock) % 4 == 0) {
hen = (100 - 7 * cock) / 4;
// 母鸡数量不能为负
if (hen >= 0) {
chick = 100 - cock - hen;
// 小鸡数量也必须是非负的3的倍数(这里由数学推导保证,但检查一下更严谨)
if (chick >= 0 && chick % 3 == 0) {
printf("%d\t%d\t%d\n", cock, hen, chick);
}
}
}
}
return 0;
}
代码解析:
- 这个版本只需要一个
for循环,效率更高。 - 核心判断是
if ((100 - 7 * cock) % 4 == 0),它直接检查hen的计算结果是否为整数。 - 如果是整数,我们计算出
hen和chick,并打印结果。
运行结果
两种代码的运行结果是完全相同的:
百钱买百鸡问题的所有可能解:
--------------------------------
公鸡 母鸡 小鸡
--------------------------------
0 25 75
4 18 78
8 11 81
12 4 84
这四组就是所有满足条件的购买方案。
对于 "C语言 pets 100" 这个问题:
- 本质是“百钱买百鸡”,一个经典的穷举算法问题。
- 暴力枚举法 是最直接的入门解法,容易理解和实现,适合初学者掌握循环和条件判断。
- 数学推导法 是更优化的解法,通过建立方程减少了循环次数,体现了算法优化的思想,适合有一定数学基础的学习者。
- 这个问题很好地展示了C语言中
for循环、if条件判断、取模运算 和整数除法 等核心概念的应用。
