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

688 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 第2课编程题教师版
## 本课知识点覆盖表
| 核心知识点 | GESP等级 | 对应课堂题 |
|---|---|---|
| 函数声明、定义与调用 | 二级必考 | 课堂1 |
| 形参与实参、值传递、返回值、提前 `return` | 二级必考 | 课堂2 |
| `void` 函数与函数重载 | 二级 | 课堂3 |
| 局部变量、全局变量与作用域 | 二级必考 | 课堂4 |
> 难度均为本课内部相对难度,参考程序采用 C++17。
## 一、课堂练习4题
### 课堂1三个数的最大公约数【适中】
**题目描述:** 编写函数 `gcd(a,b)`,使用辗转相除法求两个正整数的最大公约数。在 `main` 前写函数声明、`main` 后写函数定义,连续调用两次求三个数的最大公约数。
**输入格式:** 输入三个正整数。
**输出格式:** 三个数的最大公约数。
**数据范围:** `1≤a,b,c≤10^9`
**输入样例:**
```text
12 18 30
```
**输出样例:**
```text
6
```
**解题思路:** 先求 `gcd(a,b)`,再把结果与 `c` 求最大公约数,体会函数声明、定义和多次调用。
**参考代码:**
```cpp
#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)`。输入 `L``R`,输出闭区间 `[L,R]` 中素数的个数。
**输入格式:** 输入区间的左端点和右端点。
**输出格式:** 素数个数。
**数据范围:** `1≤L≤R≤10000`
**输入样例:**
```text
10 20
```
**输出样例:**
```text
4
```
**解题思路:** 每个实参传入函数后复制给形参 `n`;找到因数时提前返回 `false`,全部检查后返回 `true`
**参考代码:**
```cpp
#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≤9``1≤n≤50`
**输入样例:**
```text
7 * 4
```
**输出样例:**
```text
77777
****
```
**解题思路:** 两个函数同名但参数列表不同,构成函数重载;只负责输出,因此返回类型为 `void`
**参考代码:**
```cpp
#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`
**输入样例:**
```text
3
2 5 1
```
**输出样例:**
```text
2 7 8
```
**解题思路:**`::total` 指明全局变量;局部变量仅在函数的大括号内有效。
**参考代码:**
```cpp
#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`
**输入样例:**
```text
4 6
```
**输出样例:**
```text
24
```
**解题思路:** 函数接收两个形参,返回它们的乘积。
**参考代码:**
```cpp
#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`。主函数据此输出 `YES``NO`
**输入格式:** 输入一个整数。
**输出格式:** `YES``NO`
**数据范围:** `-10^9≤n≤10^9`
**输入样例:**
```text
18
```
**输出样例:**
```text
YES
```
**解题思路:** 判断 `n%2==0` 并直接返回结果。
**参考代码:**
```cpp
#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`
**输入样例:**
```text
5
```
**输出样例:**
```text
-----
```
**解题思路:** 输出操作封装进 `void` 函数。
**参考代码:**
```cpp
#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`
**输入样例:**
```text
-23
```
**输出样例:**
```text
23
```
**解题思路:** 负数返回 `-n`,否则返回 `n`
**参考代码:**
```cpp
#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`
**输入样例:**
```text
5026
```
**输出样例:**
```text
13
```
**解题思路:** 在函数内循环取个位并累加。
**参考代码:**
```cpp
#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`
**输入样例:**
```text
12 18
```
**输出样例:**
```text
36
```
**解题思路:** 先求最大公约数,再利用 `lcm=a/gcd*b`,体现函数之间相互调用。
**参考代码:**
```cpp
#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`
**输入样例:**
```text
2 4 7
```
**输出样例:**
```text
28
```
**解题思路:** 用不同参数个数区分两个同名函数。
**参考代码:**
```cpp
#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)` 判断正整数是否为回文数,输出 `YES``NO`
**输入格式:** 输入一个正整数。
**输出格式:** `YES``NO`
**数据范围:** `1≤n≤10^9`
**输入样例:**
```text
12321
```
**输出样例:**
```text
YES
```
**解题思路:** 在函数中备份原数并构造反转数,最后比较。
**参考代码:**
```cpp
#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`
**输入样例:**
```text
15
```
**输出样例:**
```text
3 5
5 7
11 13
```
**解题思路:** 枚举较小的数 `p`,同时调用两次 `isPrime`
**参考代码:**
```cpp
#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`
**输入样例:**
```text
10 15
```
**输出样例:**
```text
3
```
**解题思路:** 1015 的各位和依次为 16其中 2、3、5 是素数。让 `countGood` 调用 `digitSum`,再把结果交给 `isPrime`
**参考代码:**
```cpp
#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;
}
```
**易错提醒:** 分清每个函数的单一职责;不要在多个位置重复编写同一段逻辑。