习题4-6 水仙花数 (20 分)(算法提炼)详解!

浙大版《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)函数,导致在线编译系统计算超时,通过其他小伙伴的测试,发现若使用自定义的求幂函数即刻成功同个在线测试。关于定义函数 是教材第五章的知识。

版本1测试耗时

版本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;
}
版本2测试耗时(自定义求幂函数p()与版本1调用math.h的时间差异)

版本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;
}

可以看出新版的性能提升了不少,主要区别就是调用自带

版本1测试耗时
版本2测试耗时(自定义求幂函数p()与版本1调用math.h的时间差异)
版本3测试耗时(部分调用pow函数)

 

效率最高的版本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

 


  • 11
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值