返回 课程

信奥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.4.1 计数、求和与求乘积的结合应用

在实际问题中,我们经常需要同时使用计数、求和和求乘积这三种操作。下面我们通过一系列简单的实验来综合应用这些操作。

16.4.2 实验一:计算并统计1~n中偶数的总和及个数

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

样例输入

10

样例输出

30
5

代码实现

#include <iostream>
using namespace std;

int main() {
    int n;
    cin >> n;
    
    int sum = 0;  // 总和变量初始化为0
    int cnt = 0;  // 计数变量初始化为0
    int i = 2;    // 从第一个偶数2开始循环
    
    while(i <= n) {  // 循环到n
        sum += i;  // 加到总和中
        cnt++;     // 计数加1
        i += 2;    // 增加2,保持i为偶数
    }
    
    cout << sum << endl;  // 输出总和
    cout << cnt;          // 输出个数
    return 0;
}

思考

  1. 对于n=10,有哪些偶数?它们的总和和个数是多少?
  2. 为什么循环从i=2开始,而不是从i=1开始?

解释

  1. 1到10中的偶数有:2, 4, 6, 8, 10,共5个,总和为2+4+6+8+10=30。
  2. 循环从2开始是因为2是第一个偶数,并且通过每次加2,可以直接遍历所有偶数,不需要判断奇偶性。

16.4.3 实验二:计算并统计1~n中与7无关的数的总和及个数

题目描述: 输入一个正整数n(n<9999),计算并输出1到n中与7无关的数的总和及个数。与7无关的数是指既不包含数字7也不是7的倍数的数。

样例输入

20

样例输出

174
16

代码实现(方法一)

#include <iostream>
using namespace std;

int main() {
    int n;
    cin >> n;
    
    int sum = 0;  // 总和变量初始化为0
    int cnt = 0;  // 计数变量初始化为0
    int i = 1;    // 从1开始循环
    
    while(i <= n) {  // 循环到n
        // 提取各位数字
        int g = i % 10;          // 个位
        int s = i / 10 % 10;     // 十位
        int b = i / 100 % 10;    // 百位
        int q = i / 1000 % 10;   // 千位
        
        // 判断是否与7无关
        if(g != 7 && s != 7 && b != 7 && q != 7 && i % 7 != 0) {
            sum += i;  // 加到总和中
            cnt++;     // 计数加1
        }
        
        i++;  // 循环变量加1
    }
    
    cout << sum << endl;  // 输出总和
    cout << cnt;          // 输出个数
    return 0;
}

代码实现(方法二)

#include <iostream>
using namespace std;

int main() {
    int n;                // 定义变量n
    cin >> n;             // 输入整数n
    
    int sum = 0;          // 总和变量初始化为0
    int cnt = 0;          // 计数变量初始化为0
    int i = 1;            // 从1开始循环
    
    while(i <= n) {       // 循环到n
        // 提取各位数字
        int g = i % 10;           // 个位
        int s = i / 10 % 10;      // 十位
        int b = i / 100 % 10;     // 百位
        int q = i / 1000 % 10;    // 千位
        
        // 使用逻辑非判断是否与7无关
        if(!(g == 7 || s == 7 || b == 7 || q == 7 || i % 7 == 0)) {
            sum += i;     // 加到总和中
            cnt++;        // 计数加1
        }
        
        i++;              // 循环变量加1
    }
    
    cout << sum << endl;  // 输出总和
    cout << cnt;          // 输出个数
    return 0;
}

代码实现(方法三)

#include <iostream>
using namespace std;

int main() {
    int n;                // 定义变量n
    cin >> n;             // 输入整数n
    
    int sum_z = 0;        // 所有数的总和
    int sum = 0;          // 与7有关的数的总和
    int cnt = 0;          // 与7有关的数的个数
    int i = 1;            // 从1开始循环
    
    while(i <= n) {       // 循环到n
        sum_z += i;       // 累加所有数的总和
        
        // 提取各位数字
        int g = i % 10;           // 个位
        int s = i / 10 % 10;      // 十位
        int b = i / 100 % 10;     // 百位
        int q = i / 1000 % 10;    // 千位
        
        // 判断是否与7有关
        if(g == 7 || s == 7 || b == 7 || q == 7 || i % 7 == 0) {
            sum += i;     // 加到与7有关的总和中
            cnt++;        // 与7有关的计数加1
        }
        
        i++;              // 循环变量加1
    }
    
    cout << (sum_z - sum) << endl;  // 输出与7无关的数的总和
    cout << (n - cnt);              // 输出与7无关的数的个数
    return 0;
}

思考

  1. 什么是与7无关的数?这三种方法如何实现这个判断?
  2. 方法一和方法二在逻辑表达上有什么区别?
  3. 方法三为什么要先计算所有数的总和和与7有关的数的总和?
  4. 哪种方法在代码复杂度和执行效率上更有优势?

解释

  1. 与7无关的数是指既不包含数字7也不是7的倍数的数。方法一使用多个与(&&)条件直接判断所有位数不是7且不能被7整除;方法二使用逻辑非(!)和逻辑或(||)的组合间接判断;方法三则通过计算所有与7有关的数,然后用总体减去这部分来获得结果。
  2. 方法一和方法二表达的是同样的逻辑,但使用了不同的表达方式。方法一使用了多个与(&&)条件:g != 7 && s != 7 && b != 7 && q != 7 && i % 7 != 0,直接表述”各位都不是7且不是7的倍数”;方法二使用了逻辑非(!)和逻辑或(||)的组合:!(g == 7 || s == 7 || b == 7 || q == 7 || i % 7 == 0),表述”不是(任一位是7或是7的倍数)”,根据德摩根定律,这两种表达是等价的。
  3. 方法三采用了一种间接思路:先计算1到n的所有数的总和(sum_z)和总个数(n),再计算与7有关的数的总和(sum)和个数(cnt),最后通过相减得到与7无关的数的总和(sum_z-sum)和个数(n-cnt)。这种方法展示了解决问题的另一种思维方式:”计算补集再相减”,有时比直接计算目标集合更简单高效。
  4. 从代码复杂度看,方法二略优于方法一,因为表达更简洁;从执行效率角度,三种方法的时间复杂度都是O(n),但方法三需要额外的加法操作来计算所有数的总和,理论上效率略低。不过,方法三思路独特,在某些情况下(比如已知所有数的总和时)可能会更有优势。

总之,这三种方法展示了同一问题的不同解决思路,体现了计算机思维的多样性和灵活性。方法一直观易懂,方法二代码简洁,方法三思路独特,各有特点。

16.4.4 实验三:计算并统计m~n中3的倍数的个数和总和

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

样例输入

1 10

样例输出

3
18

代码实现

#include <iostream>
using namespace std;

int main() {
    int m, n;
    cin >> m >> n;
    
    // 找到第一个大于等于m的3的倍数
    int start = m;
    if(start % 3 != 0) {
        start = start + (3 - start % 3);
    }
    
    int sum = 0;  // 总和变量初始化为0
    int cnt = 0;  // 计数变量初始化为0
    int i = start;  // 从第一个3的倍数开始
    
    while(i <= n) {  // 循环到n
        sum += i;  // 加到总和中
        cnt++;     // 计数加1
        i += 3;    // 增加3,保持i为3的倍数
    }
    
    cout << cnt << endl;  // 输出个数
    cout << sum;          // 输出总和
    return 0;
}

思考

  1. 对于样例输入1 10,3的倍数有哪些?它们的总和和个数是多少?
  2. 代码如何找到第一个大于等于m的3的倍数?

解释

  1. 1到10中的3的倍数有:3, 6, 9,共3个,总和为3+6+9=18。
  2. 通过计算start % 3的余数,然后加上(3 – 余数),就能找到下一个3的倍数。

16.4.5 实验四:计算并统计m~n中奇数和偶数的个数和总和

题目描述: 输入两个正整数m和n(保证m <= n),分别计算并输出m到n之间(包括m和n)所有奇数和偶数的个数和总和。

样例输入

1 10

样例输出

5 25
5 30

代码实现

#include <iostream>
using namespace std;

int main() {
    int m, n;
    cin >> m >> n;
    
    int odd_sum = 0;   // 奇数总和
    int odd_cnt = 0;   // 奇数个数
    int even_sum = 0;  // 偶数总和
    int even_cnt = 0;  // 偶数个数
    int i = m;         // 从m开始循环
    
    while(i <= n) {  // 循环到n
        if(i % 2 == 0) {  // 如果i是偶数
            even_sum += i;  // 加到偶数总和
            even_cnt++;     // 偶数计数加1
        } else {           // 如果i是奇数
            odd_sum += i;   // 加到奇数总和
            odd_cnt++;      // 奇数计数加1
        }
        i++;  // 循环变量加1
    }
    
    cout << odd_cnt << " " << odd_sum << endl;  // 输出奇数个数和总和
    cout << even_cnt << " " << even_sum;        // 输出偶数个数和总和
    return 0;
}

思考

  1. 对于样例输入1 10,奇数和偶数分别有哪些?
  2. 如何优化这个算法,避免在循环中判断奇偶性?

解释

  1. 1到10中的奇数有:1, 3, 5, 7, 9,共5个,总和为25;偶数有:2, 4, 6, 8, 10,共5个,总和为30。
  2. 可以分别从第一个奇数和第一个偶数开始,使用两个循环,每次增加2,分别遍历所有奇数和偶数。

16.4.6 实验五:计算并统计某范围内能被3或5整除的数

题目描述: 输入两个正整数L和R(保证L <= R),计算并输出L到R之间(包括L和R)能被3整除或能被5整除的数的个数和总和。

样例输入

1 20

样例输出

10
98

代码实现

#include <iostream>
using namespace std;

int main() {
    int L, R;
    cin >> L >> R;
    
    int sum = 0;  // 总和变量初始化为0
    int cnt = 0;  // 计数变量初始化为0
    int i = L;    // 从L开始循环
    
    while(i <= R) {  // 循环到R
        // 判断是否能被3整除或能被5整除
        if(i % 3 == 0 || i % 5 == 0) {
            sum += i;  // 加到总和中
            cnt++;     // 计数加1
        }
        i++;  // 循环变量加1
    }
    
    cout << cnt << endl;  // 输出个数
    cout << sum;          // 输出总和
    return 0;
}

思考

  1. 对于样例输入1 20,哪些数能被3整除或能被5整除?
  2. 如果一个数同时能被3和5整除,会被计算几次?

解释

  1. 能被3整除的数有:3, 6, 9, 12, 15, 18;能被5整除的数有:5, 10, 15, 20。去重后共10个数,总和为3+5+6+9+10+12+15+18+20=98。
  2. 如果一个数同时能被3和5整除(如15),在我们的算法中只会被计算一次,因为我们使用的是逻辑或(||)操作。

16.4.7 实验六:计算并统计某范围内数字的平方和与立方和

题目描述: 输入两个正整数x和y(保证x <= y),计算并输出x到y之间(包括x和y)所有数的平方和与立方和。

样例输入

1 3

样例输出

14
36

代码实现

#include <iostream>
using namespace std;

int main() {
    int x, y;
    cin >> x >> y;
    
    int sum2 = 0;  // 平方和
    int sum3 = 0;  // 立方和
    int i = x;     // 从x开始循环
    
    while(i <= y) {  // 循环到y
        sum2 += i * i;      // 加上i的平方
        sum3 += i * i * i;  // 加上i的立方
        i++;  // 循环变量加1
    }
    
    cout << sum2 << endl;  // 输出平方和
    cout << sum3;          // 输出立方和
    return 0;
}

思考

  1. 对于样例输入1 3,各数的平方和立方分别是多少?
  2. 这个问题是否可以使用数学公式直接计算?

解释

  1. 1到3的平方和为1²+2²+3²=1+4+9=14,立方和为1³+2³+3³=1+8+27=36。
  2. 是的,平方和可以用公式n(n+1)(2n+1)/6计算,立方和可以用公式[n(n+1)/2]²计算。但这里我们有范围x到y,需要进行适当调整。

16.4.8 实验七:综合计数、求和与求乘积

题目描述: 输入两个正整数A和B(保证A <= B且B <= 20),计算并输出以下值:

  1. A到B之间(包括A和B)能被3整除的数的个数
  2. A到B之间能被3整除的数的总和
  3. A到B之间能被3整除的数的乘积

样例输入

1 10

样例输出

3
18
162

代码实现

#include <iostream>
using namespace std;

int main() {
    int A, B;
    cin >> A >> B;
    
    int cnt = 0;   // 计数变量初始化为0
    int sum = 0;   // 总和变量初始化为0
    int prod = 1;  // 乘积变量初始化为1
    int i = A;     // 从A开始循环
    
    while(i <= B) {  // 循环到B
        // 判断是否能被3整除
        if(i % 3 == 0) {
            cnt++;      // 计数加1
            sum += i;   // 加到总和中
            prod *= i;  // 乘到乘积中
        }
        i++;  // 循环变量加1
    }
    
    cout << cnt << endl;  // 输出个数
    cout << sum << endl;  // 输出总和
    cout << prod;         // 输出乘积
    return 0;
}

思考

  1. 对于样例输入1 10,能被3整除的数有哪些?
  2. 如果范围内没有能被3整除的数,乘积的结果应该是什么?

解释

  1. 1到10中能被3整除的数有:3, 6, 9,共3个数,总和为18,乘积为3×6×9=162。
  2. 如果范围内没有能被3整除的数,乘积应该保持初始值1。

16.4.9 实验八:简单的综合应用

题目描述: 输入三个正整数m、n和k(保证m <= n且k > 0),计算并输出m到n之间(包括m和n)能被k整除的数的以下信息:

  1. 能被k整除的数的个数
  2. 能被k整除的数的总和
  3. 能被k整除的数的乘积

样例输入

1 10 2

样例输出

5
30
3840

代码实现

#include <iostream>
using namespace std;

int main() {
    int m, n, k;
    cin >> m >> n >> k;
    
    int cnt = 0;   // 计数变量初始化为0
    int sum = 0;   // 总和变量初始化为0
    int prod = 1;  // 乘积变量初始化为1
    int i = m;     // 从m开始循环
    
    while(i <= n) {  // 循环到n
        // 判断是否能被k整除
        if(i % k == 0) {
            cnt++;      // 计数加1
            sum += i;   // 加到总和中
            prod *= i;  // 乘到乘积中
        }
        i++;  // 循环变量加1
    }
    
    cout << cnt << endl;  // 输出个数
    cout << sum << endl;  // 输出总和
    cout << prod;         // 输出乘积
    return 0;
}

思考

  1. 对于样例输入1 10 2,能被2整除的数有哪些?
  2. 如何优化这个算法,减少循环次数?

解释

  1. 1到10中能被2整除的数有:2, 4, 6, 8, 10,共5个数,总和为30,乘积为2×4×6×8×10=3840。
  2. 可以从第一个能被k整除的数开始,每次增加k,直接遍历所有能被k整除的数。