一、题目
编程求解百钱买百鸡问题。百钱买百鸡问题:“鸡翁一,值钱五;鸡母一,值钱三;鸡雏三,值钱一。百钱买百鸡,问鸡翁、母、雏各几只?
二、算法。
1.暴力枚举法一
由题目可知鸡翁、母、雏共有三个未知数,设为i,j,k。接下来要求解i、j、k的值分别是多少。因为已知当i>20时不满足百钱条件;当i>34时不满足百钱的条件;而k>100则不满足百鸡的条件,已知范围并且想要遍历得出所有的可能,所以使用for循环嵌套。这时可以使用条件语句if来筛选出需要的结果,百鸡也就是i+j+k等于100,而百钱也就是i*5+j*3+k*1/3等于100,不过由于整型变量会产生误差,比如10/3=3,所以我们使用浮点数i*5.0+j*3.0+k*1.0/3==100.0。代码如下:
#include <stdio.h>
int main(){
int x,y,z;
int i,j,k;
for(i=0;i<=20;i++){
for(j=0;j<=33;j++){
for(k=0;k<=100;k++){
if((i+j+k==100)&&(i*5.0+j*3.0+k*1.0/3==100.0)){
printf("%d %d %d\n",i,j,k);
}
}
}
}
return 0;
}
2.暴力法二
在法一的基础上进行优化,法一的时间复杂度为O(n^3),现在仅仅只是100以内的数据量,如果是万钱万鸡甚至是百万钱百万鸡,这时候的计算量将是10^12甚至是10^18,导致超时。所以我们必须优化。我们可以知道我们可以减少一些不必要的遍历,由于必须是100只鸡,所以在知道i,j的数值时,我们可以由i+j+k=100得出k=100-j-k。这时候我们就不需要再遍历k的数值,时间复杂度为O(n^2),代码如下:
#include <stdio.h>
int main(){
int x,y,z;
int i,j,k;
for(i=0;i<=20;i++){
for(j=0;j<=33;j++){
k=100-i-j;
if(i*5.0+j*3.0+k*1.0/3==100.0){
printf("%d %d %d\n",i,j,k);
}
}
}
return 0;
}
3.递推法
首先我们先看一道经典的鸡兔同笼问题,例如35个头,94只脚,问鸡、兔各多少只?一般最直接的方法是穷举所有的情况,根据所有的结果筛选鸡、兔的数量,代码如下:
#include <stdio.h>
int main(){
int i,j;
for(i=0;i<35;i++){
for(j=0;j<35;j++){
if((i+j==35)&&(i*2+j*4==94)){
printf("鸡=%d,兔=%d",i,j);
}
}
}
return 0;
}
但是时间复杂度为O(n^2),有没有更快速的方法?我们可以把问题转换为一个数学问题,我们可以得到两条式子鸡+兔=35,鸡*2+兔*4=94。我们根据数学原理可以知道两个完全不同的式子可以算出两个未知数的值,所以我们可以用兔=(脚-头*2)/2,算出兔子的数量,再用35-兔子的数量=鸡的数量,代码如下:
#include <stdio.h>
int main(){
double i,j;
int foot,head;
scanf("%d %d",&foot,&head);
j=(foot-head*2)/2.0;
i=35.0-j;
if(j==(foot-head)/2*1.0){
printf("鸡=%d,兔=%d",i,j);
}
else{
printf("error");
}
return 0;
}
这时的时间复杂度从O(n^2)变为O(1)大大提升了效率。同样的我们也可以使用这个方法提升效率,由两个不同的式子可以推出两个未知数。而百钱买百鸡是两条式子有三个未知数,这时只需要固定一个数的数值就可以变为两条式子有两个未知数,可以使用鸡兔同笼问题的方法,把O(n^3)变为O(n)。代码如下:
#include <stdio.h>
int main(){
int x,y,z;
int i,j,k;
for(i=0;i<=20;i++){
k=75+3*i/4;
j=100-i-k;
printf("%d %d %d\n",i,j,k);
}
return 0;
}
三、结果
0 25 75
4 18 78
8 11 81
12 4 84