# 第11课:综合练习 ## 教学目标 - 混合题型训练,覆盖一二级核心知识点 - 查漏补缺,找到薄弱环节 - 提高解题速度和准确率 ## 核心考点:混合题的拆解与算法组合 ### 考什么 综合题不会直接告诉你使用哪一课的知识,而是把输入输出、分支、循环、数组、字符串等组合起来。考试重点是从题意中识别若干小任务,为每个小任务选择已经学过的代码模式,再按正确顺序连接。 ### 必须理解 先把题目拆成“输入什么→处理什么→输出什么”三段。处理部分继续拆成动作,例如“遍历数组→筛选满足条件的元素→打擂台求最值→输出下标”。每个动作只解决一个问题,再把前一步结果交给下一步。读到“每个”通常想到遍历,读到“最大/最小”想到打擂台,读到“出现次数”想到计数,读到“如果/否则”想到分支。组合算法时要明确数据在各步骤中的含义,不能边读题边盲写代码。 ### 核心写法 ```text 1. 圈出输入、输出和数据范围 2. 把处理过程拆成若干小任务 3. 为每个小任务选择代码模板 4. 确定各步骤先后与中间变量 5. 写代码后用样例和边界值检查 ``` 例如“找数组中出现次数最多的数”可拆成:数组输入 + 频次统计 + 打擂台 + 输出答案。 ### 常见错误 - 一看到长题目就直接写代码,没有先拆任务。 - 只完成主算法,漏掉输入格式、并列规则或输出顺序。 - 不区分中间变量的含义,把下标、数值和次数混用。 - 只测试题目样例,不测试空边界、最小值、最大值或并列情况。 ### 判断是否掌握 拿到一道混合题,能在动手前写出清晰的三到五步伪代码,并能指出每一步使用了哪种基础结构或算法模板。 --- ## 题目一:成绩等级判断(分支 + 输入输出) ### 核心代码(一句话) > 多分支判断的核心:**`if - else if - else` 链条**,从大到小或从小到大依次判断,每种情况一个分支。 ### 自然语言思路 输入一个 0-100 的成绩,输出对应的等级:90-100 → A,80-89 → B,70-79 → C,60-69 → D,60以下 → E。 第一步,接收输入 `score`。 第二步,从高分往低分判断:`if (score >= 90)` → A,`else if (score >= 80)` → B,依次类推。 第三步,最后一个 `else` 兜底,对应 E 等级。 第四步,注意判断顺序:如果从小到大判断(先 `score < 60`),要看清楚边界别重叠。 > GESP 考试中这类题会以"程序阅读"或"编程题"形式出现,要注意 if-else 的匹配逻辑。 ### 伪代码 ``` 1. 输入 score 2. if score >= 90: 输出 "A" 3. else if score >= 80: 输出 "B" 4. else if score >= 70: 输出 "C" 5. else if score >= 60: 输出 "D" 6. else: 输出 "E" ``` ### 真实代码 ```cpp #include using namespace std; int main() { int score; cin >> score; if (score >= 90) { cout << "A" << endl; } else if (score >= 80) { cout << "B" << endl; } else if (score >= 70) { cout << "C" << endl; } else if (score >= 60) { cout << "D" << endl; } else { cout << "E" << endl; } return 0; } ``` ### 注意点 1. **判断顺序很重要**:从大到小写最清晰,因为 `else if` 意味着"前面条件都不满足"。如果从小到大写,要注意边界。 2. **边界值**:90 是 A,89 是 B。如果写成 `score > 90` 就会把 90 分的人划到 B,检查时要留意。 3. **输入范围**:题目保证输入在 0-100,但实际考试中如果输入 101 按上面代码会输出 "A"(因为 ≥90)。严谨的做法是加范围检查。 --- ## 题目二:数组求最大值及下标(数组 + 循环 + 打擂台) ### 核心代码(一句话) > 带下标记录的打擂台:**用一个变量记最大值,另一个变量记下标**,每次换擂主时两个一起更新。 ### 自然语言思路 输入 n 个数,输出最大值和它第一次出现的下标。 第一步,读入数组。 第二步,设 `maxVal = arr[0]`,`maxIndex = 0`,两个变量一起初始化为第一个元素的值和下标。 第三步,从 `i = 1` 开始遍历,如果 `arr[i] > maxVal`(严格大于),则同时更新 `maxVal = arr[i]` 和 `maxIndex = i`。 第四步,遍历结束,输出最大值和下标。 > 注意是"严格大于" `>`,不是 `>=`。如果写成 `>=`,遇到重复最大值时会记录最后一次出现的位置。 ### 伪代码 ``` 1. 输入 n 2. 读入数组 arr[n] 3. maxVal = arr[0], maxIndex = 0 4. for i = 1 到 n-1: 5. if arr[i] > maxVal: // 严格大于 6. maxVal = arr[i] 7. maxIndex = i 8. 输出 maxVal 和 maxIndex ``` ### 真实代码 ```cpp #include using namespace std; int main() { int n; cin >> n; int arr[n]; for (int i = 0; i < n; i++) { cin >> arr[i]; } int maxVal = arr[0]; int maxIndex = 0; for (int i = 1; i < n; i++) { if (arr[i] > maxVal) { maxVal = arr[i]; maxIndex = i; } } cout << "最大值:" << maxVal << ",下标:" << maxIndex << endl; return 0; } ``` ### 注意点 1. **`>` vs `>=`**:用 `>` 保留第一次出现的下标,用 `>=` 保留最后一次出现的下标。看题目要求。 2. **maxIndex 初始值**:必须和 maxVal 的初始值对应。如果 maxVal 从 arr[0] 开始,maxIndex 就是 0。 3. **循环从 1 开始**:因为 arr[0] 已经是初始擂主了。 --- ## 题目三:字符串分类统计(string + 字符判断 + 多计数器) ### 核心代码(一句话) > 字符串分类统计的核心:**遍历 + 多分支判断 + 多个计数器**,每个 `if` 分支对应一个计数器。 ### 自然语言思路 输入一行字符串,统计其中大写字母、小写字母、数字、其他字符(含空格)各有多少个。 第一步,用 `getline(cin, s)` 读入整行(因为可能含空格)。 第二步,设四个计数器 `upper, lower, digit, other` 都初始为 0。 第三步,遍历每个字符 `s[i]`,用 `isupper`、`islower`、`isdigit` 判断类型,对应计数器 +1。 第四步,都不满足的归入 `other`。 第五步,输出四个统计结果。 第六步,注意空格也算"其他",如果不想算空格可以先单独判断 `s[i] == ' '`。 ### 伪代码 ``` 1. getline 读取字符串 s 2. upper = 0, lower = 0, digit = 0, other = 0 3. for i = 0 到 s.length()-1: 4. if isupper(s[i]): upper++ 5. else if islower(s[i]): lower++ 6. else if isdigit(s[i]): digit++ 7. else: other++ 8. 输出四个计数结果 ``` ### 真实代码 ```cpp #include #include #include using namespace std; int main() { string s; getline(cin, s); int upper = 0, lower = 0, digit = 0, other = 0; for (int i = 0; i < s.length(); i++) { if (isupper(s[i])) { upper++; } else if (islower(s[i])) { lower++; } else if (isdigit(s[i])) { digit++; } else { other++; } } cout << "大写:" << upper << ",小写:" << lower << ",数字:" << digit << ",其他:" << other << endl; return 0; } ``` ### 注意点 1. **`getline` 不是 `cin`**:`cin >> s` 遇空格就停,"Hello World" 只会读到 "Hello"。 2. **`isupper` 等函数需要 ``**:别忘了加这个头文件。 3. **else 分支的归属**:空格、标点符号都会进 `other`。如果题目要求单独统计空格,需要在 `else` 前加 `else if (s[i] == ' ')`。 4. **空的字符串**:如果什么都没输入,四个计数器都是 0,输出全零,不会出错。 --- ## 限时练习(课堂完成) ### 练习组一(限时 30 分钟) 1. 输入两个数,输出较大的数(基础输入输出 + if) 2. 输入 n,输出 n 的阶乘 `1 × 2 × 3 × ... × n`(循环 + 累乘) 3. 输入一个字符串,判断是否为回文(string + 循环) ### 练习组二(限时 30 分钟) 1. 输入 n 个数,求平均值(数组 + 求和 + 浮点数) 2. 找出 100 以内的所有素数(循环 + 判断素数) 3. 输入一个正整数,输出它的逆序数(如 12345 → 54321)(数位拆解) ### 练习组三(限时 30 分钟) 1. 输入年份,判断是否为闰年(能被 4 整除且不能被 100 整除,或能被 400 整除) 2. 输入 n,打印左下直角三角形(嵌套循环) 3. 输入一个字符串,统计其中数字的个数(string + isdigit) --- ## 解题技巧 ### 1. 仔细审题 - 看清输入输出格式(空格、换行、大小写) - 注意数据范围(决定用 `int` 还是 `long long`) - 理解题目要求(从小到大输出还是从大到小) ### 2. 先想后写 - 在草稿纸上写出核心步骤 - 用伪代码搭好骨架 - 再动手写真实代码 ### 3. 测试验证 - 先用样例数据测试 - 再试边界情况(0、1、最大值、最小值) - 检查输出格式是否完全一致 ### 4. 时间管理 - 先做简单的题,拿稳基础分 - 不会的题先跳过,不要死磕 - 留 10 分钟检查 --- ## 常见错误检查清单 在交卷前逐条检查: - [ ] 头文件是否包含(``、``、``、`` 等) - [ ] `using namespace std;` 是否写了 - [ ] 变量是否初始化(特别是累加器、计数器) - [ ] 数组下标是否越界(从 0 到 n-1) - [ ] 循环边界是否正确(`<` vs `<=`) - [ ] 输出格式是否正确(空格、换行、中文/英文标点) - [ ] 是否处理了边界情况(0、1、负数等) - [ ] 整数除法是否需要转浮点数 - [ ] string 输入是否用了 `getline`(含空格时) --- ## 本课打油诗 > 综合练习查漏洞,各种题型过一遍。 > 先审题意后动手,伪代码里思路现。 > 边界条件要想全,样例测完再交卷。 > 时间分配有策略,不会先跳回头见。 --- ## 课后作业 1. 补完课堂限时练习中未完成的题目 2. 整理自己的错题本,写出错误原因和正确解法 3. 在洛谷上完成 GESP 一级真题中的 3 道编程题