Files
project1/第05课-自定义函数/编程题-教师版.md

13 KiB
Raw Permalink Blame History

第2课编程题教师版

本课知识点覆盖表

核心知识点 GESP等级 对应课堂题
函数声明、定义与调用 二级必考 课堂1
形参与实参、值传递、返回值、提前 return 二级必考 课堂2
void 函数与函数重载 二级 课堂3
局部变量、全局变量与作用域 二级必考 课堂4

难度均为本课内部相对难度,参考程序采用 C++17。

一、课堂练习4题

课堂1三个数的最大公约数【适中】

题目描述: 编写函数 gcd(a,b),使用辗转相除法求两个正整数的最大公约数。在 main 前写函数声明、main 后写函数定义,连续调用两次求三个数的最大公约数。

输入格式: 输入三个正整数。

输出格式: 三个数的最大公约数。

数据范围: 1≤a,b,c≤10^9

输入样例:

12 18 30

输出样例:

6

解题思路: 先求 gcd(a,b),再把结果与 c 求最大公约数,体会函数声明、定义和多次调用。

参考代码:

#include <iostream>
using namespace std;

int gcd(int a, int b);

int main() {
    int a, b, c;
    cin >> a >> b >> c;
    cout << gcd(gcd(a, b), c);
    return 0;
}

int gcd(int a, int b) {
    while (b != 0) {
        int r = a % b;
        a = b;
        b = r;
    }
    return a;
}

易错提醒: 函数声明必须与定义的返回类型、函数名和参数列表一致。

课堂2统计区间内的素数【适中】

题目描述: 编写有参数、有返回值的函数 isPrime(n)。输入 LR,输出闭区间 [L,R] 中素数的个数。

输入格式: 输入区间的左端点和右端点。

输出格式: 素数个数。

数据范围: 1≤L≤R≤10000

输入样例:

10 20

输出样例:

4

解题思路: 每个实参传入函数后复制给形参 n;找到因数时提前返回 false,全部检查后返回 true

参考代码:

#include <iostream>
using namespace std;

bool isPrime(int n) {
    if (n < 2) return false;
    for (int i = 2; i * i <= n; i++) {
        if (n % i == 0) return false;
    }
    return true;
}

int main() {
    int L, R, count = 0;
    cin >> L >> R;
    for (int x = L; x <= R; x++) {
        if (isPrime(x)) count++;
    }
    cout << count;
    return 0;
}

易错提醒: 1 不是素数;普通值传递不会修改主函数中的实参。

课堂3统一格式输出【适中】

题目描述: 重载函数 printLine:一个版本接收一个整数并输出由该整数重复 5 次组成的一行;另一个版本接收一个字符和次数,输出指定长度的一行。两个函数都没有返回值。

输入格式: 依次输入一个整数、一个字符和字符的重复次数。

输出格式: 第一行输出 5 个 x;第二行输出 n 个字符 c

数据范围: 0≤x≤91≤n≤50

输入样例:

7 * 4

输出样例:

77777
****

解题思路: 两个函数同名但参数列表不同,构成函数重载;只负责输出,因此返回类型为 void

参考代码:

#include <iostream>
using namespace std;

void printLine(int x) {
    for (int i = 0; i < 5; i++) cout << x;
    cout << "\n";
}

void printLine(char c, int n) {
    for (int i = 0; i < n; i++) cout << c;
    cout << "\n";
}

int main() {
    int x, n;
    char c;
    cin >> x >> c >> n;
    printLine(x);
    printLine(c, n);
    return 0;
}

易错提醒: 重载依据参数数量或类型区分,不能只靠返回类型区分。

课堂4全班累计加分【适中】

题目描述: 全局变量 total 表示全班累计加分。编写 addScore(x) 修改它;函数内部再定义同名局部变量 total,演示局部变量优先。每次调用后输出全局累计值。

输入格式: 第一行输入加分次数;第二行输入每次的加分值。

输出格式: 输出每次加分后的全局累计值。

数据范围: 1≤n≤20,每次加分 110

输入样例:

3
2 5 1

输出样例:

2 7 8

解题思路:::total 指明全局变量;局部变量仅在函数的大括号内有效。

参考代码:

#include <iostream>
using namespace std;

int total = 0;

void addScore(int x) {
    int total = x;
    ::total += total;
}

int main() {
    int n, x;
    cin >> n;
    for (int i = 0; i < n; i++) {
        cin >> x;
        addScore(x);
        if (i) cout << " ";
        cout << total;
    }
    return 0;
}

易错提醒: 局部变量会遮住同名全局变量;实际编程应尽量避免故意重名,本题只用于理解作用域。

二、课后作业3题

课后1求长方形面积【简单】

题目描述: 编写函数 area(a,b) 返回长方形面积。

输入格式: 输入两个正整数。

输出格式: 长方形面积。

数据范围: 1≤a,b≤10000

输入样例:

4 6

输出样例:

24

解题思路: 函数接收两个形参,返回它们的乘积。

参考代码:

#include <iostream>
using namespace std;

int area(int a, int b) {
    return a * b;
}

int main() {
    int a, b;
    cin >> a >> b;
    cout << area(a, b);
    return 0;
}

易错提醒: 有返回值的函数不能漏写 return

课后2判断偶数【简单】

题目描述: 编写布尔函数 isEven(n),偶数返回 true,奇数返回 false。主函数据此输出 YESNO

输入格式: 输入一个整数。

输出格式: YESNO

数据范围: -10^9≤n≤10^9

输入样例:

18

输出样例:

YES

解题思路: 判断 n%2==0 并直接返回结果。

参考代码:

#include <iostream>
using namespace std;

bool isEven(int n) {
    return n % 2 == 0;
}

int main() {
    int n;
    cin >> n;
    cout << (isEven(n) ? "YES" : "NO");
    return 0;
}

易错提醒: 判断相等使用 ==,不是赋值符号 =

课后3打印分隔线【简单】

题目描述: 编写无返回值函数 printLine(n),输出 n 个减号。

输入格式: 输入一个整数。

输出格式: 一行 n-

数据范围: 1≤n≤100

输入样例:

5

输出样例:

-----

解题思路: 输出操作封装进 void 函数。

参考代码:

#include <iostream>
using namespace std;

void printLine(int n) {
    for (int i = 0; i < n; i++) cout << "-";
}

int main() {
    int n;
    cin >> n;
    printLine(n);
    return 0;
}

易错提醒: void 函数不能把调用结果赋给变量。

三、扩展练习7题

扩展1绝对值函数【基础综合】

题目描述: 不使用标准库 abs,编写函数 myAbs(n) 返回整数的绝对值。

输入格式: 输入一个整数。

输出格式: n 的绝对值。

数据范围: -10^9≤n≤10^9

输入样例:

-23

输出样例:

23

解题思路: 负数返回 -n,否则返回 n

参考代码:

#include <iostream>
using namespace std;

int myAbs(int n) {
    if (n < 0) return -n;
    return n;
}

int main() {
    int n;
    cin >> n;
    cout << myAbs(n);
    return 0;
}

易错提醒: 每条可能的执行路线都应有返回值。

扩展2数字各位和【基础综合】

题目描述: 编写函数 digitSum(n),返回正整数各位数字之和。

输入格式: 输入一个正整数。

输出格式: 各位数字之和。

数据范围: 1≤n≤10^9

输入样例:

5026

输出样例:

13

解题思路: 在函数内循环取个位并累加。

参考代码:

#include <iostream>
using namespace std;

int digitSum(int n) {
    int sum = 0;
    while (n > 0) {
        sum += n % 10;
        n /= 10;
    }
    return sum;
}

int main() {
    int n;
    cin >> n;
    cout << digitSum(n);
    return 0;
}

易错提醒: 函数内修改形参 n 不会改变主函数中的实参。

扩展3最小公倍数【中等】

题目描述: 编写 gcd(a,b)lcm(a,b) 两个函数,输出两数的最小公倍数。

输入格式: 输入两个正整数。

输出格式: 最小公倍数。

数据范围: 1≤a,b≤10^6,答案不超过 10^9

输入样例:

12 18

输出样例:

36

解题思路: 先求最大公约数,再利用 lcm=a/gcd*b,体现函数之间相互调用。

参考代码:

#include <iostream>
using namespace std;

int gcd(int a, int b) {
    while (b != 0) {
        int r = a % b;
        a = b;
        b = r;
    }
    return a;
}

long long lcm(int a, int b) {
    return 1LL * a / gcd(a, b) * b;
}

int main() {
    int a, b;
    cin >> a >> b;
    cout << lcm(a, b);
    return 0;
}

易错提醒: 先除后乘并使用 long long,可降低中间结果溢出的风险。

扩展4重载求面积【中等】

题目描述: 重载函数 area:接收一个整数时返回正方形面积;接收两个整数时返回长方形面积。输入类型编号及边长后输出面积。

输入格式: 先输入图形类型;类型为 1 时再输入一个边长,类型为 2 时再输入长和宽。

输出格式: 图形面积。

数据范围: 边长为 110000

输入样例:

2 4 7

输出样例:

28

解题思路: 用不同参数个数区分两个同名函数。

参考代码:

#include <iostream>
using namespace std;

int area(int side) {
    return side * side;
}

int area(int length, int width) {
    return length * width;
}

int main() {
    int type, a, b;
    cin >> type >> a;
    if (type == 1) cout << area(a);
    else {
        cin >> b;
        cout << area(a, b);
    }
    return 0;
}

易错提醒: 两个重载版本不能拥有完全相同的参数列表。

扩展5回文数函数【中等】

题目描述: 编写函数 isPalindrome(n) 判断正整数是否为回文数,输出 YESNO

输入格式: 输入一个正整数。

输出格式: YESNO

数据范围: 1≤n≤10^9

输入样例:

12321

输出样例:

YES

解题思路: 在函数中备份原数并构造反转数,最后比较。

参考代码:

#include <iostream>
using namespace std;

bool isPalindrome(int n) {
    int original = n;
    int reversed = 0;
    while (n > 0) {
        reversed = reversed * 10 + n % 10;
        n /= 10;
    }
    return reversed == original;
}

int main() {
    int n;
    cin >> n;
    cout << (isPalindrome(n) ? "YES" : "NO");
    return 0;
}

易错提醒: 必须备份原数;形参被拆完后会变成 0。

扩展6连续素数对【略难】

题目描述: 两个相差 2 的素数称为孪生素数。编写 isPrime,输出 [2,n] 中所有孪生素数对,每对占一行;若没有,输出 NONE

输入格式: 输入一个整数。

输出格式: 所有满足条件的 p p+2

数据范围: 2≤n≤100000

输入样例:

15

输出样例:

3 5
5 7
11 13

解题思路: 枚举较小的数 p,同时调用两次 isPrime

参考代码:

#include <iostream>
using namespace std;

bool isPrime(int n) {
    if (n < 2) return false;
    for (int i = 2; i * i <= n; i++) {
        if (n % i == 0) return false;
    }
    return true;
}

int main() {
    int n;
    bool found = false;
    cin >> n;
    for (int p = 2; p + 2 <= n; p++) {
        if (isPrime(p) && isPrime(p + 2)) {
            cout << p << " " << p + 2 << "\n";
            found = true;
        }
    }
    if (!found) cout << "NONE";
    return 0;
}

易错提醒: 枚举上界必须保证 p+2≤n;无解情况不能漏掉。

扩展7函数流水线【略难】

题目描述: 编写三个函数:digitSum(n) 求各位和,isPrime(n) 判断素数,countGood(L,R) 统计区间中“各位和为素数”的整数个数。

输入格式: 输入区间的左端点和右端点。

输出格式: 满足条件的整数个数。

数据范围: 1≤L≤R≤100000

输入样例:

10 15

输出样例:

3

解题思路: 1015 的各位和依次为 16其中 2、3、5 是素数。让 countGood 调用 digitSum,再把结果交给 isPrime

参考代码:

#include <iostream>
using namespace std;

int digitSum(int n) {
    int sum = 0;
    while (n > 0) {
        sum += n % 10;
        n /= 10;
    }
    return sum;
}

bool isPrime(int n) {
    if (n < 2) return false;
    for (int i = 2; i * i <= n; i++) {
        if (n % i == 0) return false;
    }
    return true;
}

int countGood(int L, int R) {
    int count = 0;
    for (int x = L; x <= R; x++) {
        if (isPrime(digitSum(x))) count++;
    }
    return count;
}

int main() {
    int L, R;
    cin >> L >> R;
    cout << countGood(L, R);
    return 0;
}

易错提醒: 分清每个函数的单一职责;不要在多个位置重复编写同一段逻辑。