Files
project1/第09课-枚举模拟/编程题-教师版.md

677 lines
16 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.
# 第8课编程题教师版
## 本课知识点覆盖表
| 核心知识点 | 等级定位 | 对应课堂题 |
|---|---|---|
| 单变量枚举、范围与验证条件 | 三级工具,服务一二级循环分支 | 课堂1 |
| 双变量枚举、利用约束缩小范围 | 三级工具,服务简单数学 | 课堂2 |
| 状态变量、按顺序更新 | 三级工具,服务循环模拟 | 课堂3 |
| 周期过程模拟、取模与分支 | 三级工具,服务综合判断 | 课堂4 |
## 一、课堂练习4题
### 课堂1鸡兔同笼【适中】
**题目描述:** 笼中只有鸡和兔。输入头数与脚数,输出鸡和兔的数量;没有合法方案时输出 `NONE`
**输入格式:** 输入动物总数和脚的总数。
**输出格式:** 输出鸡的数量和兔的数量,或输出 `NONE`
**数据范围:** 动物总数为 11000脚数为非负整数。
**输入样例:**
```text
10 28
```
**输出样例:**
```text
6 4
```
**解题思路:** 枚举鸡的数量,兔的数量由总数减去鸡数得到,再验证脚数。
**参考代码:**
```cpp
#include <iostream>
using namespace std;
int main() {
int heads, feet;
cin >> heads >> feet;
bool found = false;
for (int chicken = 0; chicken <= heads; chicken++) {
int rabbit = heads - chicken;
if (chicken * 2 + rabbit * 4 == feet) {
cout << chicken << " " << rabbit;
found = true;
break;
}
}
if (!found) cout << "NONE";
return 0;
}
```
**易错提醒:** 枚举范围必须包含 0 和动物总数;题目只需一个方案,找到后可以结束循环。
### 课堂2硬币组合【适中】
**题目描述:** 用 1 元、2 元和 5 元硬币凑出指定金额,要求硬币总数固定。输出所有方案,每行依次输出三种硬币数量。
**输入格式:** 输入目标金额和硬币总数。
**输出格式:** 输出全部合法方案;没有方案时输出 `NONE`
**数据范围:** 目标金额和硬币总数均为 1100。
**输入样例:**
```text
10 4
```
**输出样例:**
```text
1 2 1
```
**解题思路:** 枚举 1 元和 2 元硬币数量5 元硬币数量由总数直接计算,再检查金额。
**参考代码:**
```cpp
#include <iostream>
using namespace std;
int main() {
int money, total;
cin >> money >> total;
bool found = false;
for (int one = 0; one <= total; one++) {
for (int two = 0; two + one <= total; two++) {
int five = total - one - two;
if (one + two * 2 + five * 5 == money) {
cout << one << " " << two << " " << five << "\n";
found = true;
}
}
}
if (!found) cout << "NONE";
return 0;
}
```
**易错提醒:** 第三种硬币无需再枚举;题目要求全部方案,找到后不能提前结束。
### 课堂3斐波那契数列【适中】
**题目描述:** 数列前两项为 1之后每项等于前两项之和。输入项数输出前若干项。
**输入格式:** 输入需要输出的项数。
**输出格式:** 输出数列前若干项,以空格分隔。
**数据范围:** 项数为 145。
**输入样例:**
```text
6
```
**输出样例:**
```text
1 1 2 3 5 8
```
**解题思路:** 用两个变量保存连续两项;先计算下一项,再按顺序更新两个变量。
**参考代码:**
```cpp
#include <iostream>
using namespace std;
int main() {
int n;
long long first = 1, second = 1;
cin >> n;
for (int i = 1; i <= n; i++) {
if (i > 1) cout << " ";
cout << first;
long long next = first + second;
first = second;
second = next;
}
return 0;
}
```
**易错提醒:** 更新顺序不能颠倒,必须先用旧值计算下一项。
### 课堂4交通灯状态【适中】
**题目描述:** 交通灯按“绿灯 30 秒、黄灯 5 秒、红灯 25 秒”循环。第 1 秒为绿灯。输入秒数,输出当时颜色以及该颜色已经持续的秒数。
**输入格式:** 输入一个正整数,表示经过的秒数。
**输出格式:** 输出颜色和当前颜色已持续的秒数。
**数据范围:** 秒数不超过十亿。
**输入样例:**
```text
34
```
**输出样例:**
```text
YELLOW 4
```
**解题思路:** 一个周期共 60 秒,将时间映射到 160再按三个区间判断。
**参考代码:**
```cpp
#include <iostream>
using namespace std;
int main() {
long long second;
cin >> second;
int position = (second - 1) % 60 + 1;
if (position <= 30)
cout << "GREEN " << position;
else if (position <= 35)
cout << "YELLOW " << position - 30;
else
cout << "RED " << position - 35;
return 0;
}
```
**易错提醒:** 第 60 秒仍在本周期,第 61 秒才回到绿灯第 1 秒。
## 二、课后作业3题
### 课后1满足条件的倍数【简单】
**题目描述:** 输出指定范围内所有能被 3 整除但不能被 5 整除的整数。
**输入格式:** 输入范围的左端点和右端点。
**输出格式:** 按从小到大输出所有满足条件的整数;没有则输出 `NONE`
**数据范围:** 左端点不大于右端点,绝对值不超过一万。
**输入样例:**
```text
1 20
```
**输出样例:**
```text
3 6 9 12 18
```
**解题思路:** 枚举范围内每个整数,用一个条件验证。
**参考代码:**
```cpp
#include <iostream>
using namespace std;
int main() {
int left, right;
bool found = false;
cin >> left >> right;
for (int number = left; number <= right; number++) {
if (number % 3 == 0 && number % 5 != 0) {
if (found) cout << " ";
cout << number;
found = true;
}
}
if (!found) cout << "NONE";
return 0;
}
```
**易错提醒:** 范围包含两个端点;两个条件必须同时满足。
### 课后2小球反弹高度【简单】
**题目描述:** 小球从指定高度落下,每次反弹到上一次高度的一半。输入初始高度和反弹次数,输出最后一次反弹高度,保留两位小数。
**输入格式:** 输入初始高度和反弹次数。
**输出格式:** 输出最后一次反弹高度,保留两位小数。
**数据范围:** 初始高度为正数,反弹次数为 030。
**输入样例:**
```text
100 3
```
**输出样例:**
```text
12.50
```
**解题思路:** 每次循环把当前高度除以 2按步骤模拟。
**参考代码:**
```cpp
#include <iostream>
#include <iomanip>
using namespace std;
int main() {
double height;
int times;
cin >> height >> times;
for (int i = 1; i <= times; i++) height /= 2;
cout << fixed << setprecision(2) << height;
return 0;
}
```
**易错提醒:** 反弹 0 次时高度保持初始值。
### 课后3机器人走直线【简单】
**题目描述:** 机器人初始位置为 0。输入若干条指令正数表示向右移动负数表示向左移动。输出最终位置。
**输入格式:** 第一行输入指令数量;第二行输入每条指令的移动距离。
**输出格式:** 输出机器人最终位置。
**数据范围:** 指令数量为 1100每次移动距离绝对值不超过一千。
**输入样例:**
```text
5
3 -2 4 -6 1
```
**输出样例:**
```text
0
```
**解题思路:** 用一个变量记录位置,按输入顺序累加每次移动。
**参考代码:**
```cpp
#include <iostream>
using namespace std;
int main() {
int n, position = 0;
cin >> n;
for (int i = 0; i < n; i++) {
int move;
cin >> move;
position += move;
}
cout << position;
return 0;
}
```
**易错提醒:** 负数已经表示向左,不需要再额外减一次。
## 三、扩展练习7题
### 扩展1三位水仙花数【基础综合】
**题目描述:** 输出指定范围内所有三位水仙花数。水仙花数等于各位数字立方和。
**输入格式:** 输入范围的左端点和右端点。
**输出格式:** 输出所有水仙花数;没有则输出 `NONE`
**数据范围:** 输入范围位于 100999。
**输入样例:**
```text
100 400
```
**输出样例:**
```text
153 370 371
```
**解题思路:** 枚举每个三位数,拆出百位、十位和个位后验证。
**参考代码:**
```cpp
#include <iostream>
using namespace std;
int main() {
int left, right;
bool found = false;
cin >> left >> right;
for (int number = left; number <= right; number++) {
int a = number / 100;
int b = number / 10 % 10;
int c = number % 10;
if (a * a * a + b * b * b + c * c * c == number) {
if (found) cout << " ";
cout << number;
found = true;
}
}
if (!found) cout << "NONE";
return 0;
}
```
**易错提醒:** 十位数字应先除以 10 再取余数。
### 扩展2百钱买百鸡【中等】
**题目描述:** 公鸡每只 5 元,母鸡每只 3 元,小鸡 3 只 1 元。用 100 元恰好买 100 只鸡,输出全部方案。
**输入格式:** 本题没有输入。
**输出格式:** 每行依次输出公鸡、母鸡和小鸡数量。
**数据范围:** 三种鸡的数量均为非负整数。
**输入样例:**
```text
```
**输出样例:**
```text
0 25 75
4 18 78
8 11 81
12 4 84
```
**解题思路:** 枚举公鸡和母鸡,小鸡由总数计算;为避免小数,用“小鸡数量能被 3 整除”验证价格。
**参考代码:**
```cpp
#include <iostream>
using namespace std;
int main() {
for (int rooster = 0; rooster <= 20; rooster++) {
for (int hen = 0; hen <= 33; hen++) {
int chick = 100 - rooster - hen;
if (chick >= 0 && chick % 3 == 0 &&
rooster * 5 + hen * 3 + chick / 3 == 100) {
cout << rooster << " " << hen << " " << chick << "\n";
}
}
}
return 0;
}
```
**易错提醒:** 小鸡数量必须非负且能被 3 整除。
### 扩展3数字谜【中等】
**题目描述:** 找出所有两位数,使它等于各位数字之和的 4 倍,并按从小到大输出。
**输入格式:** 本题没有输入。
**输出格式:** 输出全部满足条件的两位数。
**数据范围:** 枚举 1099。
**输入样例:**
```text
```
**输出样例:**
```text
12 24 36 48
```
**解题思路:** 枚举两位数并拆分十位和个位,验证等式。
**参考代码:**
```cpp
#include <iostream>
using namespace std;
int main() {
bool first = true;
for (int number = 10; number <= 99; number++) {
int tens = number / 10;
int ones = number % 10;
if (number == 4 * (tens + ones)) {
if (!first) cout << " ";
cout << number;
first = false;
}
}
return 0;
}
```
**易错提醒:** 枚举顺序已经从小到大,不需要任何排序。
### 扩展4日期的下一天【中等】
**题目描述:** 输入一个日期,输出下一天。需要正确处理月末、年末和闰年二月。
**输入格式:** 输入年份、月份和日期。
**输出格式:** 输出下一天的年份、月份和日期。
**数据范围:** 输入保证是合法日期,年份为 19999。
**输入样例:**
```text
2024 2 29
```
**输出样例:**
```text
2024 3 1
```
**解题思路:** 先根据月份和闰年确定本月天数,日期加一后按顺序处理月进位和年进位。
**参考代码:**
```cpp
#include <iostream>
using namespace std;
int main() {
int year, month, day;
cin >> year >> month >> day;
int days;
if (month == 2) {
bool leap = year % 400 == 0 ||
(year % 4 == 0 && year % 100 != 0);
days = leap ? 29 : 28;
} else if (month == 4 || month == 6 ||
month == 9 || month == 11) {
days = 30;
} else {
days = 31;
}
day++;
if (day > days) {
day = 1;
month++;
if (month > 12) {
month = 1;
year++;
}
}
cout << year << " " << month << " " << day;
return 0;
}
```
**易错提醒:** 必须先处理日期进位,再处理月份和年份进位。
### 扩展5比赛积分模拟【中等】
**题目描述:** 一支球队进行若干场比赛,胜得 3 分、平得 1 分、负得 0 分。输入每场进球数和失球数,输出总积分及胜、平、负场次。
**输入格式:** 第一行输入比赛场数;随后每行输入一场比赛的进球数和失球数。
**输出格式:** 依次输出总积分、胜场、平场和负场。
**数据范围:** 比赛场数为 1100进球数和失球数为非负整数。
**输入样例:**
```text
3
2 1
0 0
1 3
```
**输出样例:**
```text
4 1 1 1
```
**解题思路:** 按场次模拟,根据比分三分支更新积分和统计量。
**参考代码:**
```cpp
#include <iostream>
using namespace std;
int main() {
int n, points = 0, win = 0, draw = 0, lose = 0;
cin >> n;
for (int i = 0; i < n; i++) {
int scored, conceded;
cin >> scored >> conceded;
if (scored > conceded) {
points += 3;
win++;
} else if (scored == conceded) {
points++;
draw++;
} else {
lose++;
}
}
cout << points << " " << win << " " << draw << " " << lose;
return 0;
}
```
**易错提醒:** 三种比赛结果互斥,使用多层分支。
### 扩展6约瑟夫报数简化版【略难】
**题目描述:** 若干名同学围成一圈,从 1 号开始依次报数,报到指定数字的人离开,继续从下一人报数,直到只剩一人。输出最后留下的编号。
**输入格式:** 输入同学人数和离开所对应的报数。
**输出格式:** 输出最后留下的同学编号。
**数据范围:** 同学人数为 1100报数为 1100。
**输入样例:**
```text
5 3
```
**输出样例:**
```text
4
```
**解题思路:** 用数组记录是否仍在圈中,循环移动当前位置,只对未离开的人计数,报到指定数字时标记离开。
**参考代码:**
```cpp
#include <iostream>
using namespace std;
int main() {
int n, limit;
bool alive[101] = {false};
cin >> n >> limit;
for (int i = 1; i <= n; i++) alive[i] = true;
int remaining = n, position = 0, count = 0;
while (remaining > 1) {
position = position % n + 1;
if (!alive[position]) continue;
count++;
if (count == limit) {
alive[position] = false;
remaining--;
count = 0;
}
}
for (int i = 1; i <= n; i++)
if (alive[i]) cout << i;
return 0;
}
```
**易错提醒:** 离开的人不再参与报数;位置到末尾后要回到 1 号。
### 扩展7自动售货机【略难】
**题目描述:** 售货机初始余额为 0。依次处理若干操作正数表示投入金额-1 表示购买 3 元商品;-2 表示购买 5 元商品。余额足够才扣款并增加购买次数,否则记录一次失败。输出最终余额、成功购买次数和失败次数。
**输入格式:** 第一行输入操作数量;第二行输入所有操作代码。
**输出格式:** 依次输出最终余额、成功购买次数和失败次数。
**数据范围:** 操作数量为 11000投入金额为 1100。
**输入样例:**
```text
7
5 -1 -2 8 -2 -1 1
```
**输出样例:**
```text
3 3 1
```
**解题思路:** 按输入顺序模拟。投入金额直接增加余额;购买操作先确定价格,再判断余额是否足够。
**参考代码:**
```cpp
#include <iostream>
using namespace std;
int main() {
int n, balance = 0, success = 0, failure = 0;
cin >> n;
for (int i = 0; i < n; i++) {
int operation;
cin >> operation;
if (operation > 0) {
balance += operation;
} else {
int price = (operation == -1) ? 3 : 5;
if (balance >= price) {
balance -= price;
success++;
} else {
failure++;
}
}
}
cout << balance << " " << success << " " << failure;
return 0;
}
```
**易错提醒:** 失败购买不能扣除余额;操作必须严格按输入顺序处理。