No.11-No.15 C语言从初识到竞赛精通

No.11-No.15 C语言从初识到竞赛精通

| No.11 打印出100-999范围内所有的“水仙花数”

题目分析

本题中,所谓“水仙花数”是指一个 三位数 ,其各位数字立方和等于该数,本题重点在于如何求一个数的各个位数。

对于一个三位数,我们知道: d = a * 100 + b * 10 + c * 1

在C语言中,我们有运算符: / ,以及 % ,其中 % 为求余数运算,且除法在c语言中有以下特性:

除法运算的结果与被除数的数据类型有关,如果被除数是浮点型,则除法运算之后的结果也为浮点型,若被除数为整形,则运算结果也为整形

例如: 11 / 2 = 5

在有以上内容做铺垫之后,我们就可以来着手设计本题的代码了:

  • 1,求百位上的数字:num / 100;例如:101 / 100 = 1
  • 2,求十位上的数字:num / 10 % 10;例如:123 / 10 = 12;12 % 10 = 9
  • 3,求个位上的数字:num % 10;例如:123 % 10 = 3

至于求一个数的三次方,我们可以使用c语言math库中自带的函数:pow,到此,万事俱备,我们直接看代码:

#include<stdio.h> 
#include<math.h>
int main(){
    int ge,shi,bai = 0;
    int cnt = 0;
    for (int i=100;i<1000;i++){
        ge = i % 10; // 求个位数
        shi = i/10 % 10; // // 求十位数
        bai = i/100; // // 求百位数
        if ((pow(ge, 3) + pow(shi, 3) + pow(bai, 3)) == i){
            printf("%d^3 + %d^3 + %d^3 = %d, %d 为水仙花数!\n", ge, shi, bai, i, i);
            cnt ++;
        }
    }
    printf("共有 %d 位水仙花数\n", cnt);
}

| No.12 将一个正整数分解质因数。例如:输入90,打印出90=2 * 3 * 3 * 5

题目分析

有很多小伙伴一上来就被这个题目的 “分解质因数” 这几个大字给唬住了,其实我们可以仔细想一想下面这个问题:

  • 有一个数,他只能被2整除
  • 有一个数,他只能被3整除
  • 有一个数,他能被4整除,则这个数必能被2整除
  • 有一个数,他只能被5整除
  • 有一个数,他能被6整除,则这个数必能被2和被3整除
  • 有一个数,他只能被7整除
  • 有一个数,他能被8整除,则这个数必能被2整除
  • 有一个数,他能被9整除,则这个数一定能被3整除
  • 。。。。

换句话说,只要我们从最小的2开始,一个一个一个数往后试,只为得到第一个能被目标数整除的因数,且这个数一定是一个质数,每得到一个质因数,则将目标数字做除法处理,即:new_num = num / prime;对目标数字进行分解,只为得到 new_num * prime = num 这个关系式,然后根据这个方式一直递推,就可以得到所有的目标 prime

有以上的推断之后,我们可以来编写代码了,其实编写代码的逻辑并不复杂

#include<stdio.h>

main() 
{ 
    int n,i; 
    printf("请输入一个数:\n"); 
    scanf("%d",&n); 
    printf("\n%d = ",n); 
    for(i=2; i<=n; i++){ // 从2开始找到能使目标数字整除的第一个因数
        while(i != n){ 
            if(n % i == 0){
                printf(" %d * ",i); 
                n=n/i;  // 对应上述内容中的new_num = num / prime
            } 
            else 
                break; 
        } 
    } 
    printf("%d",n);
}

| No.13 利用条件运算符的嵌套来完成此题:学习成绩>=90分的同学用A表示,60-89分之间的用B表示, 60分以下的用C表示。

题目分析

在本题中,题目要求我们 使用条件运算符的嵌套 来实现,在某些教材中, 条件运算符的嵌套 也称为 三元运算符 ,换言之,本题其实核心在考察我们三元运算符的使用。

考虑到这个点并不在C语言理论学习的重点内,这里我们简要的介绍一下这个点,其主要形式为: 条件表达式?表达式1:表达式2 ,其意义为:

当条件表达式成立时,返回表达式1的值,否则返回表达式2的值

并且,这里的 表达式 仍然可以继续嵌套三目运算符。那么在有了以上基础的了解之后,我们直接看代码:

#include<stdio.h>

int main(){
    int score;
    char grade;
    scanf("%d", &score);
    grade = score >= 90 ? 'A':(score >= 60 ? 'B':'C');
    printf("score: %d, grade: %c", score, grade);
}

|No.14 输入两个正整数m和n,求其最大公约数和最小公倍数。

题目分析

本题其本质上是一个数学问题,其在初等数论中作为一个比较基础的问题来论证辗转相除法及其应用,那么现在假设小伙伴们并不知道这个方法结论,我们来以一个编程学者的视角来打开这个问题。

假设有三个数,数a、数b和数c,若有 $a * b = c$,则我们可以很容易的知道:$a = c / b$,现在如果已知a和c,我们需要使用编程的方式来实现 求一个数 $x$ 使得 $ax = c$ 成立 ,我们首先能想到的方法就是 循环 ,我们可以假设循环变量i从1开始一直增加到 $c$,如果在这之中存在某个数使得 $ai = c$,则这个 i 即为所求因数。

在本题中,求最小公倍数即可使用上述方式,首先,我们假设选择 数m 作为因数之一,使循环变量从1开始自增到n,则如果存在: i * m % n == 0

则代表找到了这个最小公倍数:$i * m$

那么以上思考方向是否可以应用于求最大公因数上呢?我们都知道,最大公因数其在数学中的表示:

max( x ∈ { i | (a % i == 0 && b % i == 0)} )

因此,我们只需要找到两个目标数字中 较小 的那个,同时设定临时变量用于存储“当前最大公因数”,使其成为循环变量的最大边界值,然后持续判断循环变量 i 是否能被 数m 和 数n 同时 整除,由于作为循环变量的 i 是自小而大增加,因此,只要有新的数 i 满足上述条件,直接替换临时变量的值,最终得到的即为待求的最大公因数。

通过上述方法我们便可以实现求解两数的最大公因数了,这里我们提出一个小小的思考题:为什么一开始时需要首先找到两个目标数字中 较小 的那个呢?任意在两数中随便取一个作为循环变量的边界值是否可以呢?如果这样做,会有什么意料之外的情况呢?小伙伴们不妨动手试一试哦!

在有了以上分析之后,我们直接来看代码吧:

#include<stdio.h>

int max_(int num1, int num2){ // 求最大公约数 
    int tmp = num1 < num2 ? num1:num2;
    int max_yue = 0;
    for (int i=1; i<=tmp; i++){
        if (num1%i==0 && num2%i==0)
            max_yue = i;
    }
    return max_yue;
}

int min_(int num1, int num2){ // 求最小公倍数 
    int tmp = num1 < num2 ? num1:num2;
    for(int i=1; i<=tmp; i++){
        if (num2*i % num1 == 0){
            return num2*i;  // 最先满足的为最小公倍数,此后将不用再继续循环 
        }
    }
}

int main(){
    int nums1, nums2;
    printf("请输入两个数,以空格分割\n");
    scanf("%d %d", &nums1, &nums2);

    int res1 = max_(nums1, nums2);
    int res2 = min_(nums1, nums2);

    printf("最大公约数: %d,最小公倍数:%d", res1, res2);

}

至于本题一开始时提到的数论的实现方式,其代码实现起来也比较简单,博主还是希望小伙伴们可以自己去尝试编写,如果有困难,可以参考一下下面的代码,有其他疑问也欢迎私信博主哦!

#include<stdio.h>

main() 
{ 
    int a,b,num1,num2,temp; 
    printf("请输入两个数,以空格分隔\n"); 
    scanf("%d %d",&num1,&num2); 

    if(num1 < num2){ // 为了使用辗转相除法,应首先使两数满足一定的大小关系 
        temp = num1; 
        num1 = num2; 
        num2 = temp; 
    } 
    a = num1;
    b = num2;
    /*利用辗除法,直到b为0为止*/ 
    while(b!=0){ 
        temp = a%b; 
        a = b; 
        b = temp; 
    }

    printf("最大公因数:%d\n",a); 
    printf("最小公倍数:%d\n",num1*num2/a); 
} 

| No.15 输入一行字符,分别统计出其中英文字母、空格、数字和其它字符的个数。

题目分析

本题核心是“计数”,可能有第一次遇到这种题目的小伙伴会比较疑惑:应该怎么准确的识别出其中的各类的字符

其实在常见的英文字符中,每个字符都有其各自对应的编码,目前沿用最广泛的是 ASCII编码(美国信息交换标准代码),关于这个编码的背景这里不再详细解释,有兴趣的小伙伴可以自己去百度了解,如果有以后想往编程竞赛方向发展的同学,这里建议记住大小写英文字母对应的各个起始和终止编码哦,至少要记住在ASCII编码和字符之间转换的功能函数,在以后的比赛中很可能会遇到此类题目,但是题中一般是不会给出此类提示信息。

言归正传,本题除了小小的考察一下这个点之外,还有对略带 getchar() 函数的考察,当然也有小伙伴会选择使用变量接收输入值,再遍历该变量以统计,这样也是可以的

另外提一点,在c语言中,char类型的变量可以直接比较大小,其核心是比较字符的ASCII编码的大小,因此这个方法很多时候被用来确定在某个编码范围内的字符,并且需要注意的是,在C语言中,最终变量的数据类型取决于定义的类型,与输入的数据的类型其实没有关系,比如本题中的“数字”,在函数中定义的接收输入的变量其数据类型为char,则最终类型为char,而非整形,有小伙伴曾在类似的题中私信过博主这个问题,数字0-9其ASCII编码并非同数字0-9,而是48-57,这点在 以后的解题中需要多加注意!!!

在了解上述内容之后,我们来看代码:

#include<stdio.h>

int main(){
    int word=0, space=0, digit=0, others=0;
    while(true){
        char c = getchar(); // 逐个获取黑窗口的输入字符
        if (c == '\n') break;  // 当输入为键盘上的enter时,结束输入对象的统计
        if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'))  // 为字母
            word++;
        else if (c == ' ') // 为空格
            space ++;
        else if ('0' <= c && c <= '9') // 为数字
            digit ++;
        else
            others ++;
    }
    printf("word= %d, space= %d, digit= %d, others= %d", word, space, digit, others);
}

那么以上就是本期 C语言从初识到竞赛精通 No.11-No.15 的全部内容了,如果您在阅读本文的过程中有所收获,或者有任何宝贵的建议和想法,欢迎通过邮箱、微信或者留言等方式给我留言交流,您的每一次建议都将是我前进的动力!

上一篇
下一篇