浙大版《C语言程序设计(第3版)》题目集
习题4-6 水仙花数 (20 分)
输入格式:
输入在一行中给出一个正整数N(3≤N≤7)。
输出格式:
按递增顺序输出所有N位水仙花数,每个数字占一行。
输入样例:
3
输出样例:
153
370
371
407
补充输出样例:
三位的水仙花数共有4个:153,370,371,407;
四位的四水仙花数共有3个:1634,8208,9474;
【题意理解与分析】
本题的求解思路只能用暴力尝试法 即:一个个比对条件 看是否成立 =====》工具:利用循环遍历 =====》核心要点:需要确定循环的区间(算法1)
在暴力尝试的循环内部 我们拿到一个数
首先要 把他拆解成 n 个数位 ,每个数位 对应乘上 n 次方 (调用pow(a,b)计算)
其次:我们要把每个数位的n次方累加起来
最后比对:判断数位拆解求次方后的累加和sum的值是否和 当前暴力尝试值 相等
算法思维(题意任务拆解)
从给出的题意中,我们可以得到隐含着的关键的几个信息。
1.遍历,若给出N=3,则从100-999进行遍历,故给出N位正整数时,我们应该要找到它进行遍历操作时的范围(任务1)。
2.整数分解,将一位正整数分解,一次一次取它的/10的余数,然后进行N次幂操作。最后相加求和。值得注意的是,每次求和完毕后,应令sum求和的值重新归零。
3.N次幂求解,可以利用POW(x,y)函数也可以利用循环操作。
版本1:
#include<stdio.h>
#include<math.h>
int main(){
int n,min,max ,now,sum,temp,digit;
//n 用来保存输入数据的遍历
//min 用于保存暴力尝试的起点
//max 用于保存暴力尝试的终点
//now 用于表示当前尝试的数据
//sum 用于存储数位拆解求n次方后的累加和
//temp 由于当前尝试的now的值还要作为和sum的比较 故新建一个临时遍历 用于被逐步切分
//digit 用于存储 某个数位上的值
scanf("%d",&n);
min = pow(10 ,n -1); //循环下界确定算法
max= pow(10,n)-1; //循环上界确定算法
now = min; //当前尝试值now赋初值
while (now <= max ){
sum = 0; //每次循环 求和要重置
temp=now;
// printf("当前的尝试的值为:【%d】\n",now);
for (int i=1 ; i<=n ;i++){ //对于给出的n位数 内部循环i 时间循环取数
// printf("当前temp的值%d\n",temp);
digit = temp % 10 ; //取最后一位数据
// printf("当前digit取出的值%d\n",digit);
sum =sum + pow(digit,n); //求该位置的数据 的n次方
temp = temp /10 ; //位下一次数据截取做准备
// printf("当前sum的值%d\n",sum);
}//上面的循环主要是实现sum
if(sum == now) //核心条件判断
printf("%d\n",now);
now++; //控制while循环的变量
}
return 0;
}
数据太大超时,我们可以看到当N取6时,测试耗时就395ms,当n从4 的7ms 到 5的49 再到 6的395s 可以推测出计算7位数的代码耗时会超过2500 ms
原因分析:可能原因是每次计算都要调用 math.h 中的 pow(a,b)函数,导致在线编译系统计算超时,通过其他小伙伴的测试,发现若使用自定义的求幂函数即刻成功同个在线测试。关于定义函数 是教材第五章的知识。
版本2 自定义求幂函数 p;
#include<stdio.h>
int p(int a,int b); //自定义求幂函数的声明
int main(){
int n,min,max ,now,sum,temp,digit;
//n 用来保存输入数据的遍历
//min 用于保存暴力尝试的起点
//max 用于保存暴力尝试的终点
//now 用于表示当前尝试的数据
//sum 用于存储数位拆解求n次方后的累加和
//temp 由于当前尝试的now的值还要作为和sum的比较 故新建一个临时遍历 用于被逐步切分
//digit 用于存储 某个数位上的值
scanf("%d",&n);
min = p(10 ,n -1); //循环下界确定算法
max= p(10,n)-1; //循环上界确定算法
now = min; //当前尝试值now赋初值
while (now <= max ){
sum = 0; //每次循环 求和要重置
temp=now;
// printf("当前的尝试的值为:【%d】\n",now);
for (int i=1 ; i<=n ;i++){ //对于给出的n位数 内部循环i 时间循环取数
// printf("当前temp的值%d\n",temp);
digit = temp % 10 ; //取最后一位数据
// printf("当前digit取出的值%d\n",digit);
sum =sum + p(digit,n); //求该位置的数据 的n次方
temp = temp /10 ; //位下一次数据截取做准备
// printf("当前sum的值%d\n",sum);
}//上面的循环主要是实现sum
if(sum == now) //核心条件判断
printf("%d\n",now);
now++; //控制while循环的变量
}
return 0;
}
int p(int a,int b){ //求幂函数的定义
int pow=1,j;
for(j=1;j<=b;j++)
{
pow=pow*a;
}
return pow;
}
版本3
没学函数封装,在核心逻辑上不调用math.h中的求幂函数,而是自己编写逻辑。
#include<stdio.h>
#include<math.h>
int main(){
int n,now,sum,temp,digit; //删除简单中间变量直接在对应位置写控制表达式
scanf("%d",&n);
now = pow(10 ,n -1); //当前尝试值now赋初值
while (now < pow(10,n) ){
sum = 0; //每次循环 求和要重置
temp=now;
for (int i=1 ; i<=n ;i++){ //对于给出的n位数 内部循环i 时间循环取数
digit = temp % 10 ; //取最后一位数据
for(int j = 1 ; j<n ; j++) //在内部循环使用自己定义的求幂逻辑替代使用math.h中的pow函数
digit *=temp % 10; //防止在线评测系统检测超时 digit本身就有一次了 故总共只需循环n-1次
sum += digit; //求该位置的数据 的n次方
temp = temp /10 ; //为下一次数据截取做准备
}//上面的循环主要是实现sum
if(sum == now) //核心条件判断
printf("%d\n",now);
now++; //控制while循环的变量
}
return 0;
}
纯代码版本
#include<math.h>
int main(){
int n,now,sum,temp,digit;
scanf("%d",&n);
now = pow(10 ,n -1);
while (now < pow(10,n) ){
sum = 0;
temp=now;
for (int i=1 ; i<=n ;i++){
digit = temp % 10 ;
for(int j = 1 ; j<n ; j++)
digit *=temp % 10;
sum += digit;
temp = temp /10 ;
}
if(sum == now)
printf("%d\n",now);
now++;
}
return 0;
}
可以看出新版的性能提升了不少,主要区别就是调用自带
效率最高的版本2纯代码版
#include<stdio.h>
int p(int a,int b); //自定义求幂函数的声明
int main(){
int n,min,max ,now,sum,temp,digit;
scanf("%d",&n);
min = p(10 ,n -1); //循环下界确定算法
max= p(10,n)-1; //循环上界确定算法
now = min; //当前尝试值now赋初值
while (now <= max ){
sum = 0; //每次循环 求和要重置
temp=now;
for (int i=1 ; i<=n ;i++){
digit = temp % 10 ;
sum += p(digit,n); //求该位置的数据 的n次方
temp = temp /10 ; //位下一次数据截取做准备
}//上面的循环主要是实现sum
if(sum == now)
printf("%d\n",now);
now++;
}
return 0;
}
int p(int a,int b){
int pow=1,j;
for(j=1;j<=b;j++)
pow=pow*a;
return pow;
}
aaaaaaaaaaa