返回 课程

信奥AC之路-1级

0% 完成
0/0 步骤
  1. 第1课 开发环境与基础输出
    5 主题|小节
  2. 第2课 算术运算符
    7 主题|小节
  3. 第3课 printf与运算输出
    7 主题|小节
  4. 第4课 数的进制与拆位
    6 主题|小节
  5. 第5课 变量与基础运算
    17 主题|小节
  6. 第6课 常量与取整运算
    8 主题|小节
  7. 第7课 关系运算
    8 主题|小节
  8. 第8课 逻辑运算
    9 主题|小节
  9. 第9课 输入与计算进阶
    10 主题|小节
  10. 第10课 if语句及双分支语句
    8 主题|小节
  11. 第11课 if语句及双分支进阶
    11 主题|小节
  12. 第12课 三目运算
    9 主题|小节
  13. 第13课 多分支、多if和switch语句
    11 主题|小节
  14. 第14课 循环(基本输出)
    7 主题|小节
  15. 第15课 循环(While+If)
    8 主题|小节
  16. 第16课 循环(计数、求和、求乘积)
    10 主题|小节
  17. 第17课 循环进阶(While+)
    8 主题|小节
  18. 第18课 do-while及while其他用法
    8 主题|小节
  19. 第19课 For循环基础
    9 主题|小节
  20. 第20课 For循环进阶
    8 主题|小节
课 进展
0% 完成

16.1.1 计数的基本概念

“计数就是统计特定条件的数的个数。比如,我们要统计1到100中有多少个偶数,就可以使用循环计数来解决。”

计数的基本思路:

  1. 定义一个计数变量(通常命名为count或cnt),初始值设为0
  2. 使用循环遍历所有可能的数
  3. 在循环中判断每个数是否满足条件,如果满足,计数变量加1
  4. 循环结束后,计数变量的值就是满足条件的数的个数

16.1.2 实验一:输出1~n中所有的偶数的个数

题目描述: 输入一个正整数n,计算并输出1到n中所有偶数的个数。

样例输入

10

样例输出

5

代码实现

#include <iostream>
using namespace std;

int main() {
    int n;
    cin >> n;
    
    int cnt = 0;  // 计数变量初始化为0
    int i = 1;    // 从1开始循环
    
    while(i <= n) {  // 循环到n
        if(i % 2 == 0) {  // 判断是否为偶数
            cnt++;  // 计数加1
        }
        i++;  // 循环变量加1
    }
    
    cout << cnt;  // 输出计数结果
    return 0;
}

优化版本

#include <iostream>
using namespace std;

int main() {
    int n;
    cin >> n;
    
    int cnt = n / 2;  // 直接计算偶数个数
    
    cout << cnt;  // 输出计数结果
    return 0;
}

思考

  1. 为什么计数变量cnt的初始值为0?
  2. 优化版本为什么能直接用n/2计算偶数个数?

解释

  1. 计数变量初始化为0,因为一开始我们还没有找到任何偶数。
  2. 从1到n中,偶数的个数是n/2(如果n是偶数)或(n-1)/2(如果n是奇数)。因为整数除法会向下取整,所以无论n是奇数还是偶数,n/2都能正确计算偶数个数。

16.1.3 实验二:输出m~n中所有的奇数的个数

题目描述: 输入两个正整数m和n(m可能大于n),计算并输出m到n之间(包括m和n)所有奇数的个数。

样例输入

3 10

样例输出

4

代码实现

#include <iostream>
using namespace std;

int main() {
    int m, n;
    cin >> m >> n;
    
    // 确保m <= n
    if(m > n) {
        int t = m;
        m = n;
        n = t;
    }
    
    int cnt = 0;  // 计数变量初始化为0
    int i = m;    // 从m开始循环
    
    while(i <= n) {  // 循环到n
        if(i % 2 != 0) {  // 判断是否为奇数
            cnt++;  // 计数加1
        }
        i++;  // 循环变量加1
    }
    
    cout << cnt;  // 输出计数结果
    return 0;
}

优化版本

#include <iostream>
using namespace std;

int main() {
    int m, n;
    cin >> m >> n;
    
    // 确保m <= n
    if(m > n) {
        int t = m;
        m = n;
        n = t;
    }
    
    // 计算奇数个数
    int cnt = (n - m + 1 + (m % 2) ) / 2;  // 奇数个数计算公式
    
    cout << cnt;  // 输出计数结果
    return 0;
}

思考

  1. 为什么需要交换m和n的值?
  2. 优化版本中,奇数个数的计算公式是如何得出的?

解释

  1. 因为题目说m可能大于n,但我们需要从较小值遍历到较大值,所以如果m>n,需要交换它们的值。
  2. 从m到n共有(n-m+1)个数。如果m和n都是奇数,奇数个数为(n-m+1+1)/2;如果m和n都是偶数,奇数个数为(n-m+1)/2;如果一奇一偶,奇数个数为(n-m+1)/2。可以统一表达为(n-m+1+(m%2))/2。

16.1.4 实验三:输出m~n中所有是7的倍数的数的个数

题目描述: 输入两个正整数m和n(保证m <= n),计算并输出m到n之间(包括m和n)是7的倍数的数的个数。

样例输入

1 50

样例输出

7

代码实现

#include <iostream>
using namespace std;

int main() {
    int m, n;
    cin >> m >> n;
    
    int cnt = 0;  // 计数变量初始化为0
    int i = m;    // 从m开始循环
    
    while(i <= n) {  // 循环到n
        if(i % 7 == 0) {  // 判断是否为7的倍数
            cnt++;  // 计数加1
        }
        i++;  // 循环变量加1
    }
    
    cout << cnt;  // 输出计数结果
    return 0;
}

优化版本

#include <iostream>
using namespace std;

int main() {
    int m, n;
    cin >> m >> n;
    
    // 找到范围内的第一个7的倍数和最后一个7的倍数
    int first = (m + 6) / 7 * 7;  // 大于等于m的第一个7的倍数
    int last = n / 7 * 7;         // 小于等于n的最后一个7的倍数
    
    int cnt = 0;
    cnt = (last - first) / 7 + 1;  // 计算7的倍数的个数
    cout << cnt;  // 输出计数结果
    return 0;
}

思考

  1. 对于样例输入1 50,7的倍数有哪些?
  2. 优化版本中,如何找到范围内的第一个和最后一个7的倍数?

解释

  1. 1到50中的7的倍数有:7, 14, 21, 28, 35, 42, 49,共7个。
  2. 第一个7的倍数可以通过向上取整(m/7)然后乘以7得到,即(m+6)/77;最后一个7的倍数可以通过向下取整(n/7)然后乘以7得到,即n/77。

16.1.5 实验四:输出n的所有因数的个数

题目描述: 输入一个正整数n,计算并输出n的所有因数的个数。

样例输入

12

样例输出

6

代码实现

#include <iostream>
using namespace std;

int main() {
    int n;
    cin >> n;
    
    int cnt = 0;  // 计数变量初始化为0
    // 正整数的因数最小是1,最大是自己
    int i = 1;    // 从1开始检查 
    
    while(i <= n) {  // 检查1到n的所有数
        if(n % i == 0) {  // 判断i是否为n的因数
            cnt++;  // 计数加1
        }
        i++;  // 循环变量加1
    }
    
    cout << cnt;  // 输出计数结果
    return 0;
}

优化版本

#include <iostream>
#include <cmath>
using namespace std;

int main() {
    int n;
    cin >> n;
    
    int cnt = 0;  // 计数变量初始化为0
    int i = 1;    // 从1开始检查
    int sqrtn = sqrt(n);  // n的平方根
    
    while(i <= sqrtn) {  // 只需要检查到sqrt(n)
        if(n % i == 0) {  // 判断i是否为n的因数
            cnt++;  // i是因数,计数加1
            if(i != n / i) {  // 如果i不等于n/i
                cnt++;  // n/i也是因数,计数再加1
            }
        }
        i++;  // 循环变量加1
    }
    
    cout << cnt;  // 输出计数结果
    return 0;
}

思考

  1. 12的所有因数有哪些?
  2. 优化版本为什么只需要检查到sqrt(n)?

解释

  1. 12的因数有:1, 2, 3, 4, 6, 12,共6个。
  2. 如果i是n的因数,那么n/i也是n的因数。因此,只需要检查到sqrt(n),就能找到所有的因数对(i, n/i)。但需要注意,如果i是n的平方根,则i等于n/i,这种情况下应该只计算一次。

16.1.6 实验五:输出1~n中个位是5的数的个数

题目描述: 输入一个正整数n,计算并输出1到n中个位数字是5的数的个数。

样例输入

100

样例输出

10

代码实现

#include <iostream>
using namespace std;

int main() {
    int n;
    cin >> n;
    
    int cnt = 0;  // 计数变量初始化为0
    int i = 5;    // 从第一个个位是5的数开始
    
    while(i <= n) {  // 循环到n
        cnt++;    // 计数加1
        i += 10;  // 下一个个位是5的数
    }
    
    cout << cnt;  // 输出计数结果
    return 0;
}

思考

  1. 为什么从i=5开始,每次增加10?
  2. 对于样例输入100,个位是5的数有哪些?

解释

  1. 个位是5的数满足i%10=5,这些数分别为5, 15, 25, 35…,所以可以从5开始,每次增加10。
  2. 1到100中个位是5的数有:5, 15, 25, 35, 45, 55, 65, 75, 85, 95,共10个。

16.1.7 实验六:统计1~n中包含数字3或者5,且因数有2的数的个数

题目描述: 输入一个正整数n(n<1000),计算并输出1到n中包含数字3或者5且是2的倍数的数的个数。

样例输入

20

样例输出

2

代码实现(方法一)

#include <iostream>
using namespace std;

int main() {
    int i, n, cnt = 0;
    cin >> n;
    i = 1;
    
    while(i <= n) {  // 从1开始检查每个数
        int g = i % 10;       // 个位
        int s = i / 10 % 10;  // 十位
        int b = i / 100 % 10; // 百位
        
        // 判断是否为2的倍数且包含数字3或5
        if(i % 2 == 0 && (g == 5 || g == 3 || s == 5 || s == 3 || b == 5 || b == 3)) {
            cnt++;  // 计数加1
        }
        
        i++;  // 检查下一个数
    }
    
    cout << cnt;  // 输出计数结果
    return 0;
}

代码实现(方法二)

#include <iostream>
using namespace std;

int main() {
    int i, n, cnt = 0;
    cin >> n;
    i = 2;
    
    while(i <= n) {  // 直接从2开始,只检查偶数
        int g = i % 10;       // 个位
        int s = i / 10 % 10;  // 十位
        int b = i / 100 % 10; // 百位
        
        // 只需判断是否包含数字3或5
        if(g == 5 || g == 3 || s == 5 || s == 3 || b == 5 || b == 3) {
            cnt++;  // 计数加1
        }
        
        i += 2;  // 增加2,跳到下一个偶数
    }
    
    cout << cnt;  // 输出计数结果
    return 0;
}

思考

  1. 两种方法在实现上有什么区别?哪种更高效?
  2. 为什么方法二可以直接从2开始,每次增加2?
  3. 对于样例输入20,有哪些数同时满足这两个条件?

解释

  1. 方法一检查从1到n的每个数,然后用条件语句筛选出符合要求的数;方法二只检查偶数(从2开始,每次加2),这样就不需要额外判断是否为2的倍数。方法二更高效,因为它减少了循环次数(大约减少一半)和判断次数。
  2. 方法二从2开始,每次增加2,保证了所有检查的数都是偶数(即2的倍数)。由于题目要求数是2的倍数,这种方法避免了对非偶数的不必要检查,提高了效率。

16.1.8 实验七:输出m~n中所有的闰年的个数

题目描述: 输入两个正整数m和n(保证m < n),计算并输出m到n之间(不包括m和n)所有闰年的个数。

样例输入

2000 2020

样例输出

4

代码实现

#include <iostream>
using namespace std;

int main() {
    int m, n;
    cin >> m >> n;
    
    int cnt = 0;      // 计数变量初始化为0
    int i = m + 1;    // 从m后面的一年开始循环
    
    while(i < n) {    // 循环到n前面的一年
        // 判断是否为闰年
        if((i % 4 == 0 && i % 100 != 0) || i % 400 == 0) {
            cnt++;    // 计数加1
        }
        i++;          // 循环变量加1
    }
    
    cout << cnt;      // 输出计数结果
    return 0;
}

思考

  1. 闰年的判断条件是什么?
  2. 为什么循环从m+1开始到n-1结束?
  3. 对于样例输入2000 2020,有哪些年份被计算在内?哪些年份不被计算?

解释

  1. 闰年的判断条件是:(1)能被4整除但不能被100整除,或(2)能被400整除。
  2. 循环从m+1开始到n-1结束是因为题目要求不包括边界值m和n,只计算它们之间的闰年个数。
  3. 对于输入2000 2020,计算的年份范围是2001到2019。其中闰年有:2004、2008、2012、2016,共4个。而2000和2020虽然也是闰年,但根据题目要求不计算在内。