一、前言
这道题是C语言程序设计课程-循环章节的经典例题。
如果要看最优的解题方法,请直接看“一重循环代码实现”解题。
作者纯粹是为爱发电,整理不易。所以小可爱们动动小手,点个免费的赞吧~
以防找不到本文,收藏本文也完全不吃亏哟~
若有不妥之处,欢迎交流讨论。
二、题目
我国古代数学家张丘建在《算经》一书中曾提出过著名的“百钱买百鸡”问题,该问题叙述如下:鸡翁一,值钱五;鸡母一,值钱三;鸡雏三,值钱一;百钱买百鸡,则翁、母、雏各几何?
翻译过来的题目很简单:公鸡5文钱一只,母鸡3文钱一只,小鸡3只一文钱,用100文钱买一百只鸡,其中公鸡,母鸡,小鸡都必须要有。问公鸡,母鸡,小鸡要买多少只刚好凑足100文钱。
三、解题
(一)暴力解题(三重循环解题)
1.题目分析
通过枚举公鸡、母鸡、小鸡的数量,当满足条件:
- 小鸡是3的倍数; -> 小鸡总不可能是半只吧。
- 买公鸡、母鸡和小鸡的前刚好为100元;-> 题目中说了的。
- 公鸡、母鸡和小鸡刚好为100只。 -> 题目中说了的。
则输出每次的结果。
注意结果要换行,方便读出结果。
2.代码实现
设置变量:公鸡是a,母鸡是b,小鸡是c。
#include<stdio.h>
int main() {
int a,b,c;
//三层循环可以解决,暴力解题
for(a=1; a<=20; a++) {
for(b=1; b<=33; b++) {
for(c=1; c<=100; c++) {
if(c%3==0 && 5*a+3*b+c/3==100 && a+b+c==100) {
printf("%d %d %d\n",a,b,c);
}
}
}
}
return 0;
}
3.运行结果
执行了20×33×100=66000次循环。
(二)两重循环解题
1.题目分析
我们已经用三重循环解出了题目,那么我们可不可以少用一层循环来解出题目呢?
答案是可以的。
想一想,我们平时是怎么解决三元一次方程问题的呢?
就是先用两个变量来替换第三个变量的表达,然后进行二元一次方程的计算。
同理,在算法里面,我们也可以这样对第三个变量进行替换,来减少时间复杂度。
和上一种方法一样,条件是:
- 小鸡是3的倍数;
- 买公鸡、母鸡和小鸡的前刚好为100元;
- 公鸡、母鸡和小鸡刚好为100只。
因为小鸡的表达就是:鸡的总只数(100)-公鸡只数(a)-母鸡只数(b)。
而这个设置就隐含了,公鸡只数+母鸡只数+小鸡只数=100。
所以这道题就只有2个条件。
- 小鸡是3的倍数;
- 买公鸡、母鸡和小鸡的前刚好为100元;
2.代码实现
设置变量:公鸡是a,母鸡是b,小鸡是(100-a-b)。
#include<stdio.h>
int main() {
int a,b;
//并不仅仅是三层循环可以解决,第三个变量用其他两个变量替换,就可以优化算法
for(a=1; a<=20; a++) {
for(b=1; b<=33; b++) {
if((100-a-b)%3==0 && 5*a+3*b+(100-a-b)/3==100) {
printf("%d %d %d\n",a,b,100-a-b);
}
}
}
return 0;
}
3.运行结果
执行了20×33=660次循环。
(三)一重循环解题
1.题目分析
事情简化到两重循环已经是最简单了吗?让我们来思考一下还有没有更快的解决方案。
从第一种方案到第二种方案,我们为了更快,是从循环下手的。那么我们还可不可以再少用一层循环来解出题目呢?
答案是可以的。下面就是魔法时刻(大误):
同样是设置变量:公鸡是a,母鸡是b,小鸡是(100-a-b)。三种鸡的总价是100元,如果我们把这个式子化简,看看可以得到什么结果。推导如下:
(等会儿还会用到)
看到最后的结果,我们可以发现,用公鸡数把母鸡数给表达出来!!!
母鸡数:
我们在每次枚举公鸡数的时候,把对应的母鸡数和小鸡数都带入进去就可。
这道题有2个条件:
- 小鸡是3的倍数;
- 刚刚推导出的条件:
2.代码实现
#include<stdio.h>
int main() {
int a,b;
//通过解开方程式来找到a/b间的关系式,仅仅用一层循环就可带出答案。
for(a=1; a<=14; a++) {
b=(100-7*a)/4;
if((100-a-b)%3==0 && 7*a+4*b==100) {
printf("%d %d %d\n",a,b,100-a-b);
}
}
return 0;
}
3.运行结果
执行了20×33=14次循环。
四、三种结果分析
三种解题方法分别循环了66000次、660次、14次。这些数值差距巨大,但为了大家更直观看出这些数值的差距,下面做了一些统计图表:
这个图表看的不是很清楚,但也足以表达三重循环比其他两个所用次数更多。
下面给大家还是看一下二重循环和一重循环之间的差距吧。
综上所述,一重循环的代码为时间复杂度最小的代码。
五、后记
突然想到写这个博客,是因为想起我大一的时候上C语言程序设计课程,老师在上到循环一节介绍的这个例题,把我狠狠震惊到了。让我深刻感受到了算法的力量和美。希望这篇博客也可以给一些初学者们带来一些新的认知。