通过一道题体会算法之美——百元买鸡问题

一、前言

这道题是C语言程序设计课程-循环章节的经典例题。

如果要看最优的解题方法,请直接看“一重循环代码实现”解题。

作者纯粹是为爱发电,整理不易。所以小可爱们动动小手,点个免费的吧~

以防找不到本文,收藏本文也完全不吃亏哟~

若有不妥之处,欢迎交流讨论。

二、题目

我国古代数学家张丘建在《算经》一书中曾提出过著名的“百钱买百鸡”问题,该问题叙述如下:鸡翁一,值钱五;鸡母一,值钱三;鸡雏三,值钱一;百钱买百鸡,则翁、母、雏各几何?

翻译过来的题目很简单:公鸡5文钱一只,母鸡3文钱一只,小鸡3只一文钱,用100文钱买一百只鸡,其中公鸡,母鸡,小鸡都必须要有。问公鸡,母鸡,小鸡要买多少只刚好凑足100文钱。

三、解题

(一)暴力解题(三重循环解题)

1.题目分析

通过枚举公鸡、母鸡、小鸡的数量,当满足条件:

  1. 小鸡是3的倍数; -> 小鸡总不可能是半只吧。
  2. 买公鸡、母鸡和小鸡的前刚好为100元;-> 题目中说了的。
  3. 公鸡、母鸡和小鸡刚好为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.题目分析

我们已经用三重循环解出了题目,那么我们可不可以少用一层循环来解出题目呢?

答案是可以的。

想一想,我们平时是怎么解决三元一次方程问题的呢?

就是先用两个变量来替换第三个变量的表达,然后进行二元一次方程的计算。

同理,在算法里面,我们也可以这样对第三个变量进行替换,来减少时间复杂度。

和上一种方法一样,条件是:

  1. 小鸡是3的倍数;
  2. 买公鸡、母鸡和小鸡的前刚好为100元;
  3. 公鸡、母鸡和小鸡刚好为100只。

因为小鸡的表达就是:鸡的总只数(100)-公鸡只数(a)-母鸡只数(b)。

而这个设置就隐含了,公鸡只数+母鸡只数+小鸡只数=100。

所以这道题就只有2个条件。

  1. 小鸡是3的倍数;
  2. 买公鸡、母鸡和小鸡的前刚好为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元,如果我们把这个式子化简,看看可以得到什么结果。推导如下:

5a+3b+(100-a-b)\div3=100

\Longleftrightarrow5a+3b+\frac{100}{3}-\frac{a}{3}-\frac{b}{3}=100

\Longleftrightarrow\frac{14}{3}a+\frac{8}{3}b=\frac{200}{3}

\Longleftrightarrow14a+8b=200

\Longleftrightarrow7a+4b=100(等会儿还会用到)

\Longleftrightarrow b=(100-7a)\div4

看到最后的结果,我们可以发现,用公鸡数把母鸡数给表达出来!!!

母鸡数:b=(100-7a)\div4

我们在每次枚举公鸡数的时候,把对应的母鸡数和小鸡数都带入进去就可。

这道题有2个条件:

  1. 小鸡是3的倍数;
  2. 刚刚推导出的条件:7a+4b=100

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语言程序设计课程,老师在上到循环一节介绍的这个例题,把我狠狠震惊到了。让我深刻感受到了算法的力量和美。希望这篇博客也可以给一些初学者们带来一些新的认知。

  • 30
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值