第三章 程序的控制结构¶
基础数学知识¶
- 负数、相反数与绝对值:负数小于 \(0\);相反数相加为 \(0\);绝对值表示数到 \(0\) 的距离,例如 \(|-3.14|=3.14\)。对应 1039、1040、1043、1057。
- 区间与不等式:分清“大于”和“大于等于”,以及端点是否包含。例如 \(1<n<100\) 不包含两端,\(25\le t\le30\) 包含两端。对应 2052、2054、1044、1051、1056。
- 小数运算与公式代入:会计算小数乘除法;代入公式前,先根据条件选对计算式。对应 2055、2058、1050、1051。
- 互质与公倍数:\(3\) 与 \(5\) 互质,一个整数同时被两者整除,等价于被它们的乘积 \(15\) 整除。对应 1046。
- 用余数分类与列式推理:把金额写成 \(x=4n+r\),其中 \(0\le r<4\);每次把 \(4\) 元笔换成 \(5\) 元、\(6\) 元笔,分别多花 \(1\) 元、\(2\) 元。对应 2059。
- 路程、速度、时间与等式变形:\(t=\frac sv\),总时间还可能包含准备时间;不等式两边同乘正数,大小关系不变。对应 1050。
- 分段函数与平方:分段函数按自变量所在范围选公式;\((x-3)^2=(x-3)(x-3)\)。对应 1051。
- 向上取整:不足一份也算一份,例如 \(\lceil501/500\rceil=2\),而 \(\lceil500/500\rceil=1\)。对应 1052。
- 三角形三边关系:任意两边之和必须严格大于第三边,等于时不能构成三角形。对应 1054。
- 闰年的判定规则:按题目规则,年份能被 \(4\) 整除且不能被 \(100\) 整除,或能被 \(400\) 整除时,是闰年。对应 1055。
- 平面直角坐标:用 \((x,y)\) 表示点的位置,横纵坐标同时限制一个区域;本题正方形包含边界。对应 1056。
- 一元二次方程、平方根与判别式:方程 \(ax^2+bx+c=0\) 中 \(a\ne0\),判别式 \(\Delta=b^2-4ac\) 决定实根情况;\(\Delta\ge0\) 时用 \(x=\frac{-b\pm\sqrt\Delta}{2a}\) 求根。对应 1058。
第一节 if选择结构¶
2051:【例 3.1】偶数¶
提示 1:先判断,再决定要不要输出
输入3时,题目有没有要求输出no?
奇数就不输出,别顺手加一句no。
常见错误
不要习惯性地补上 no,题目没有要求。= 是赋值,比较相等要用 ==。
自测
输入:12 → 预期输出:yes
输入:3 → 预期输出:不输出任何字符
自己试试:自己选一个偶数、一个奇数,各试一次。
先写下哪次应该输出、哪次不该输出,再运行。
参考代码
2052:【例 3.2】范围判断¶
提示 1:端点算不算在范围里
把1、2、99、100标在数轴上,哪两个能通过?
“大于1且小于100”需要两件事同时成立。
常见错误
不能照数学写法写成 1 < n < 100,C++ 会先把 1 < n 算成 0 或 1,再与 100 比较。用 \(1,2,99,100\) 检查端点。
自测
输入:1 → 预期输出:不输出任何字符
输入:2 → 预期输出:yes
输入:99 → 预期输出:yes
输入:100 → 预期输出:不输出任何字符
自己试试:试着各选一个范围内、范围外的数。
除了这两类,两个端点要不要单独检查?
参考代码
2053:【例 3.3】三个数¶
提示 1:先固定一种大小关系
把3、1、2从大到小排列,再把两个数换成相同的,是否仍能排?
提示 2:一种顺序对应一个分支
三个整数存入a、b、c,若a最大、b其次:
仿照写其他次序,放在else if里;用>=让相等的数也有分支可走。
知识补充
swap(a, b) 会交换两个变量的值,需要 <utility>。也可以用临时变量写成 int t = a; a = b; b = t;,回顾第二章的交换值。 看懂三次比较的作用后,关上提示,换一组数自己写,再检查每次交换后的变量值。
常见错误
不要只用严格大于号列情况,否则两数相等时可能一个分支也进不去。交换法的三次比较要按顺序做;拿 \(1,3,2\) 在纸上跟一遍。
自测
输入:1 3 2 → 预期输出:3 2 1
输入:5 5 2 → 预期输出:5 5 2
输入:-3 -1 -2 → 预期输出:-1 -2 -3
输入:2000000000 2000000000 2000000000 → 预期输出:2000000000 2000000000 2000000000
自己试试:自己选三个数,让其中两个同为最大值。
调换它们的输入顺序,预期输出应该改变吗?
方法一:列出六种大小顺序
#include <iostream>
using namespace std;
int main() {
int a, b, c;
cin >> a >> b >> c;
if (a >= b && b >= c) cout << a << ' ' << b << ' ' << c;
else if (a >= c && c >= b) cout << a << ' ' << c << ' ' << b;
else if (b >= a && a >= c) cout << b << ' ' << a << ' ' << c;
else if (b >= c && c >= a) cout << b << ' ' << c << ' ' << a;
else if (c >= a && a >= b) cout << c << ' ' << a << ' ' << b;
else if (c >= b && b >= a) cout << c << ' ' << b << ' ' << a;
return 0;
}
方法二:比较并交换
方法三:总和减最大值与最小值
2054:【例 3.4】适合晨练¶
提示 1:刚好25度和30度呢
把两端点与中间温度都试一次,题目说的适宜范围包含端点。
常见错误
不要漏掉端点,也不要把 && 写成 ||,否则范围外的数也可能被判为适合。输出中的小写字母与感叹号都要保留。
自测
输入:25 → 预期输出:ok!
输入:30 → 预期输出:ok!
输入:24 → 预期输出:no!
输入:30.5 → 预期输出:no!
自己试试:在温度分界点两边各选一个数。
这两次运行,应该进入同一个分支吗?
参考代码
2055:【例 3.5】收费¶
提示 1:先确定用哪种单价
20和21分别落在哪个收费档?
这里要看清楚,选好单价以后,全部都按这个单价算。
不是只算超出的那一点。
常见错误
不要写成 20 * 1.68 + (x - 20) * 1.98,那是另一种收费规则。拿 \(20\) 与 \(21\) 分别检查单价。
自测
输入:20.00 → 预期输出:33.60
输入:21.00 → 预期输出:41.58
输入:1.50 → 预期输出:2.52
参考代码
2056:【例 3.7】最大的数¶
提示 1:让最大值待在同一位置
读入double x, y, z后,先让x和y比,谁大就把谁放进x。
常见错误
最大值不要初始设为 \(0\),三个数都为负数时会出错。多个数相等时也必须正常输出。
自测
输入:1.01 2 3.22 → 预期输出:3.22
输入:-3.5 -1.2 -2 → 预期输出:-1.2
输入:2 2 2 → 预期输出:2
自己试试:构造一组全为负数的数据。
它能检查出“最大值初始设为零”的错误吗?
参考代码
1039:判断数的正负¶
提示 1:零要单独留一条路
在数轴上标正数、0、负数,三种情况会不会重叠?
提示 2:前两种判断完,剩下是什么
n已读入;自己补最后一个分支,别把赋值=当成比较==。
常见错误
不要漏掉 \(0\),也不要输出中文解释。分别用正数、零、负数检查三个分支。
自测
输入:1 → 预期输出:positive
输入:0 → 预期输出:zero
输入:-1000000000 → 预期输出:negative
参考代码
1040:输出绝对值¶
提示 1:绝对值看离零多远
-3.14与3.14离零的距离相同,哪一种输入需要改变符号?
常见错误
不要用 int 读入浮点数,也不要把所有输入都取相反数。\(0\) 的绝对值仍是 \(0\),必须输出 0.00。
自测
输入:-3.14 → 预期输出:3.14
输入:0 → 预期输出:0.00
输入:2.5 → 预期输出:2.50
输入:-0.00 → 预期输出:0.00
自己试试:自己选一对相反数,先写出各自的预期输出。
这两次输出应该有什么关系?
方法一:分支取相反数
方法二:fabs 函数
1041:奇偶数判断¶
提示 1:余数用来区分奇偶
1、2、100除以2分别余多少?
判断偶数只需检查余数是否为0。
常见错误
“余数只能是 \(0\) 或 \(1\)”依赖本题输入为正整数。若以后允许负数,C++ 的 -3 % 2 是 \(-1\),判断奇数应写 n % 2 != 0。
自测
输入:1 → 预期输出:odd
输入:2 → 预期输出:even
输入:100 → 预期输出:even
方法一:比较余数
方法二:判断余数是否非零
方法三:按位与(拓展)
1042:奇偶 ASCII 值判断¶
提示 1:判断的是编码,不是字符长相
A的ASCII码为65,字符0的ASCII码为48,各是奇数还是偶数?
常见错误
不能先减去 '0',那是在把数字字符转换为对应数字,不是取 ASCII 值。输出是大写 YES、NO。
自测
输入:A → 预期输出:YES
输入:B → 预期输出:NO
输入:0 → 预期输出:NO
参考代码
1043:整数大小比较¶
提示 1:比较前先确保读得下
输入可能超过普通32位int的上限,两个变量该用什么类型?
提示 2:三种关系只走一种
用long long x, y读入后:
补第三种关系,输出符号本身,不是输出文字说明。
常见错误
不要把第一个数硬塞进 int。也不要一正一负地混用无符号与有符号类型,那样比较时的类型转换可能改变结果。用最大值和负数一起检查。
自测
输入:4294967295 -1 → 预期输出:>
输入:0 0 → 预期输出:=
输入:0 1 → 预期输出:<
输入:0 -2147483648 → 预期输出:>
自己试试:从题目范围内选一个 int 装不下的数,再配一个负数。
先判断大小,再检查程序能否正确读入。
参考代码
1044:判断是否为两位数¶
提示 1:从最小两位数到最大两位数
本题范围里的两位数从10到99,9和100能不能通过?
常见错误
这是判断两位正整数,不需要自行增加负数的规则。拿 \(9,10,99,100\) 检查四个临界值。
自测
输入:9 → 预期输出:0
输入:10 → 预期输出:1
输入:99 → 预期输出:1
输入:100 → 预期输出:0
参考代码
1045:收集瓶盖赢大奖¶
提示 1:只够一种也能换
10个幸运瓶盖、0个鼓励瓶盖能换吗?
再看0个幸运、20个鼓励,这两种不必同时达标。
常见错误
不要把两类数量相加后比较,也不要用 && 强迫两类都达标。“达到”包含刚好够数的情况。
自测
输入:10 0 → 预期输出:1
输入:0 20 → 预期输出:1
输入:9 19 → 预期输出:0
输入:10 20 → 预期输出:1
自己试试:试着只改变一种瓶盖的数量,让结果从不能兑换变成能兑换。
另外一种数量保持不变,方便看出变化来自哪里。
参考代码
1046:判断一个数能否同时被 3 和 5 整除¶
提示 1:同时整除需要两个零余数
15符合,9只被3整除,能通过吗?
常见错误
不能写成 ||,只被其中一个数整除不符合题意。\(0\) 除以这两个非零数余数都为 \(0\),应输出 YES。
自测
输入:15 → 预期输出:YES
输入:9 → 预期输出:NO
输入:-30 → 预期输出:YES
输入:0 → 预期输出:YES
方法一:分别判断两个除数
方法二:判断是否被 15 整除
1047:判断能否被 3,5,7 整除¶
提示 1:先列哪些除数能留下
给15分别做除以3、5、7的余数检查,应该留下几个除数?
不能只找到一个就忽略其他条件。
提示 2:先写三个都满足的情况
沿方法一继续写恰好两个、恰好一个的分支;为什么三个都满足的要放前面?
提示 3:一个都没有时才输出n
把11代入自己写的分支,最后应走到else cout << 'n';。
别把找不到写成NO,也别把输出除数写成输出商。
常见错误
只写一串单独的 else if (n % 3 == 0)、else if (n % 5 == 0) 会漏掉同时成立的情况。输出的是除数,不是商;没有一个整除时是小写 n,不是 NO。
自测
输入:105 → 预期输出:3 5 7
输入:15 → 预期输出:3 5
输入:21 → 预期输出:3 7
输入:35 → 预期输出:5 7
输入:9 → 预期输出:3
输入:25 → 预期输出:5
输入:49 → 预期输出:7
输入:11 → 预期输出:n
自己试试:能构造一个只被 \(5\) 和 \(7\) 整除、不能被 \(3\) 整除的数吗?
先用取余核对,再写出预期输出。
方法一:列出整除组合
// 把所有情况列举一遍
#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
if (n % 3 == 0 && n % 5 == 0 && n % 7 == 0) cout << 3 << ' ' << 5 << ' ' << 7;
else if (n % 3 == 0 && n % 5 == 0) cout << 3 << ' ' << 5;
else if (n % 3 == 0 && n % 7 == 0) cout << 3 << ' ' << 7;
else if (n % 5 == 0 && n % 7 == 0) cout << 5 << ' ' << 7;
else if (n % 3 == 0) cout << 3;
else if (n % 5 == 0) cout << 5;
else if (n % 7 == 0) cout << 7;
else cout << 'n';
return 0;
}
方法二:分别判断并控制空格
#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
bool flag = false;
if (n % 3 == 0) {
cout << 3;
flag = true;
}
if (n % 5 == 0) {
if (flag) cout << ' ';
cout << 5;
flag = true;
}
if (n % 7 == 0) {
if (flag) cout << ' ';
cout << 7;
flag = true;
}
if (!flag) cout << 'n';
return 0;
}
1048:有一门课不及格的学生¶
提示 1:恰好一门和至少一门不同
把两科及格、不及格的四种搭配画出来,哪两种需要输出1?
常见错误
刚好 \(60\) 分算及格。只判断 a < 60 || b < 60,会把两门都不及格也算进去。
自测
输入:59 60 → 预期输出:1
输入:60 59 → 预期输出:1
输入:59 59 → 预期输出:0
输入:60 60 → 预期输出:0
自己试试:先列出两门成绩“及格、不及格”的所有搭配。
自己为每种搭配选一组分数,看看有没有漏掉的情况。
方法一:列出两种情况
方法二:比较两个判断结果
第二节 switch语句¶
2057:【例 3.9】星期几¶
提示 1:一个数字只选一个星期
输入1时,输出Monday后还要继续执行星期二吗?
提示 2:先写一个case
读入整数n后,在switch (n)里写:
补2到7以及default;缺少break会在哪种输入上暴露问题?
常见错误
漏写 break,程序可能继续执行后面的分支。星期名称首字母大写;错误信息中的空格、感叹号也要一致。
自测
输入:1 → 预期输出:Monday
输入:7 → 预期输出:Sunday
输入:0 → 预期输出:input error!
输入:8 → 预期输出:input error!
参考代码
#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
switch (n) {
case 1: cout << "Monday"; break;
case 2: cout << "Tuesday"; break;
case 3: cout << "Wednesday"; break;
case 4: cout << "Thursday"; break;
case 5: cout << "Friday"; break;
case 6: cout << "Saturday"; break;
case 7: cout << "Sunday"; break;
default: cout << "input error!";
}
return 0;
}
2058:【例 3.10】简单计算器¶
提示 1:先看输入顺序和数的类型
2 1.2 -是两个数在前、符号在后,变量不能都用int。
提示 2:先完成减法分支
double a, b和char c已按顺序读入,放进switch (c):
补加、乘;除法分支先看b是否为0,无效符号交给default。
提示 3:把两种错误分开测
1 0 /是除零,1 0 %是无效符号。
不要在判断符号前统一检查b == 0;错误文字按题面复制核对。
常见错误
不能把这题与 1057 的整数除法混在一起:本题 7 2 / 应得到 \(3.5\)。错误运算符即使配上第二个数 \(0\),也应输出无效符号,不能先统一判除零。
自测
输入:2 1.2 - → 预期输出:0.8
输入:7 2 / → 预期输出:3.5
输入:1 0 / → 预期输出:Divided by zero!
输入:1 0 % → 预期输出:Invalid operator!
输入:3 4 * → 预期输出:12
自己试试:自己补一组还没测过的运算。
再选一个无效符号,想想它应该触发哪个分支。
方法一:switch 选择运算
#include <iostream>
using namespace std;
int main() {
double a, b;
char c;
cin >> a >> b >> c;
switch (c) {
case '+': cout << a + b; break;
case '-': cout << a - b; break;
case '*': cout << a * b; break;
case '/':
if (b == 0) cout << "Divided by zero!";
else cout << a / b;
break;
default: cout << "Invalid operator!";
}
return 0;
}
方法二:if 选择运算
#include <iostream>
using namespace std;
double a, b;
char c;
int main() {
cin >> a >> b >> c;
if (c == '+') cout << a + b;
else if (c == '-') cout << a - b;
else if (c == '*') cout << a * b;
else if (c == '/') {
if (b == 0) cout << "Divided by zero!";
else cout << a / b;
} else cout << "Invalid operator!";
return 0;
}
2059:【例 3.11】买笔¶
提示 1:先买最便宜的,再考虑换笔
每支至少4元,金额x最多能买x / 4支。
先用11元手算:两支4元笔后还剩3元,怎样换成贵一点的笔把钱用完?
提示 3:输出前核对数量和总价
用a、b、c分别记6元、5元、4元笔的数量。
先试r = 0,再补三个余数分支;别把无解金额擅自规定成输出-1。
知识补充
例如 \(11=4\times2+3\),先设想买两支 \(4\) 元笔,再分别补 \(1\) 元、\(2\) 元,变成一支 \(5\) 元笔和一支 \(6\) 元笔,仍是两支,总价正好 \(11\) 元。这里没有规定最优方案必须唯一,例如 \(10\) 元也可以用两支 \(5\) 元笔花完。
常见错误
余 \(3\) 元时需要替换两支笔,不能只减掉一支,也不能产生负的购买数量。金额 \(1,2,3,7\) 本来就无法恰好花完,题面未规定这些无解金额的输出,不能自行添加 -1 等规则;参考代码在这些金额下不输出方案。
自测
输入:4 → 预期输出:0 0 1
输入:5 → 预期输出:0 1 0
输入:6 → 预期输出:1 0 0
输入:10 → 预期输出:1 0 1
输入:11 → 预期输出:1 1 0
输入:12 → 预期输出:0 0 3
自己试试:自己选一个能恰好花完的金额,先手算一种方案。
除了核对总价,还能怎样确认笔的支数已经最多?
方法一:按余数分类
#include <iostream>
using namespace std;
int main() {
long long x;
cin >> x;
long long n = x / 4, r = x % 4;
long long a = 0, b = 0, c = n;
if (r == 1) {
if (n < 1) {
return 0;
}
b = 1;
c--;
} else if (r == 2) {
if (n < 1) {
return 0;
}
a = 1;
c--;
} else if (r == 3) {
if (n < 2) {
return 0;
}
a = b = 1;
c -= 2;
}
cout << a << ' ' << b << ' ' << c;
return 0;
}
方法二:枚举购买数量(拓展)
1049:晶晶赴约会¶
提示 1:题目问能赴约,不是有没有课
周一、三、五有课,输入3时该答YES还是NO?
提示 2:每种相等都要写完整
n已读入,剩下补YES;n == 1 || 3 || 5为什么不是三个相等判断?
常见错误
不要把有课与能赴约写反。每个等号比较都必须完整,例如不能写成 n == 1 || 3 || 5。
自测
输入:1 → 预期输出:NO
输入:3 → 预期输出:NO
输入:5 → 预期输出:NO
输入:2 → 预期输出:YES
输入:7 → 预期输出:YES
参考代码
1050:骑车与走路¶
提示 1:先把准备时间加进去
骑车比走路快,但取车、停车还要50秒。
先别急着比较,取100米,两种走法各算一次时间。
提示 2:一起乘6,避免浮点误差
距离存入dist,两种时间同时乘6:
比较较小值决定输出,最后还要留哪个相等分支?
常见错误
不要漏掉固定的 \(50\) 秒。直接比较两个浮点除法的结果,可能把本应相等的 \(100\) 米误判为不同;不要凭感觉加一个误差值,先看能否转成准确的整数比较。
自测
输入:99 → 预期输出:Walk
输入:100 → 预期输出:All
输入:101 → 预期输出:Bike
自己试试:用题目给的速度,自己算出两种方式一样快的距离。
再在它两边各选一个距离,先预测结果。
方法一:比较放大后的总时间
方法二:按距离分界点判断
1051:分段函数¶
提示 1:先选公式,再代数字
把0、5、10、19分别放到题面的三个区间中,5和10归哪一段?
提示 2:先完成第一段
读入double x,结果用double y:
补其余分支;第二段的平方必须写(x - 3) * (x - 3),不能用^。
提示 3:边界值比中间值更能查错
拿5、10各手算一次,先确认选对公式,再核对保留三位小数的输出。
常见错误
C++ 中 ^ 不能表示平方,x - 3 * x - 3 也不等于 \((x-3)^2\)。先选分支,再代入公式;拿分界点手算,而不是只测每一段中间的数。
自测
输入:0 → 预期输出:2.500
输入:5 → 预期输出:-4.000
输入:10 → 预期输出:3.500
输入:19 → 预期输出:8.000
自己试试:为每一段各选一个输入,再检查分界点。
你选的数据能区分“公式算错”和“分支选错”吗?
参考代码
1052:计算邮资¶
提示 1:先看刚好多出1克
1000克8元,1001克已经多出一份超重费。
再拿1500克、1501克算一下,就多1克,费用变不变?
提示 2:超出的重量单独算
只有n > 1000时,才进入这个分支:
w是超出的克数,k是计费份数;把k * 4加到原来的8元上。
提示 3:加急费再单独处理
加急标志为y时,再加5元。
超重又加急,两笔都要收,所以加急判断不能写成前一个判断的else if。
常见错误
不要直接用 w / 500,那样 \(1\) 到 \(499\) 克会被算成零份。加急与重量是两件事,不要写成互斥的 else if。
自测
输入:1000 n → 预期输出:8
输入:1001 n → 预期输出:12
输入:1500 n → 预期输出:12
输入:1501 y → 预期输出:21
自己试试:自己找一个邮资刚好要增加的位置,选它前后相差 \(1\) 克的两个重量。
重量不变,只改加急标志,费用又应相差多少?
参考代码
1053:最大数输出¶
提示 1:第一个输入就能当擂主
对-3 -1 -2,若把最大值先设成0,会输出哪个没出现过的数?
常见错误
不要把最大值初始设成 \(0\),全是负数时就会出错。可以先让ans = a,再分别和另外两个数比较。
自测
输入:-3 -1 -2 → 预期输出:-1
输入:5 5 5 → 预期输出:5
输入:9 2 3 → 预期输出:9
方法一:分支维护最大值
方法二:嵌套调用 max
方法三:用列表调用 max
1054:三角形判断¶
提示 1:等于两边之和会被拉直
先画边长1、2、3,三条边能围出面积吗?
需要严格大于,不是大于等于。
常见错误
必须是严格大于,等于时三点在同一直线上。未先确定最长边时,只检查 a + b > c 不够,例如 \(10,2,3\) 会暴露这个问题。
自测
输入:3 4 5 → 预期输出:yes
输入:1 2 3 → 预期输出:no
输入:10 2 3 → 预期输出:no
自己试试:自己找三条能组成三角形的边,再只改一条,让两边之和恰好等于第三边。
把最长边换到不同输入位置,结果应该改变吗?
参考代码
1055:判断闰年¶
提示 1:把整百年单独挑出来
2024、1900、2000都能被4整除,哪两个是闰年?
常见错误
只判断 % 4 == 0 会把 \(1900\) 判错;只排除整百年又会把 \(2000\) 判错。注意“不能被 \(100\) 整除”要写 % 100 != 0。
自测
输入:2024 → 预期输出:Y
输入:1900 → 预期输出:N
输入:2000 → 预期输出:Y
输入:2023 → 预期输出:N
自己试试:自己找一个能被 \(4\) 整除的整百年份。
还需要检查什么,才能写下预期结果?
方法一:组合逻辑条件
方法二:依次判断整除关系
1056:点和正方形的关系¶
提示 1:横纵坐标要分别过关
画出横纵坐标都从-1到1的正方形,边界上的点也算。
点(2,0)只满足纵坐标范围,能算里面吗?
常见错误
只判断横坐标或者只判断纵坐标,都可能把外面的点算进来。不要写严格不等号漏掉边与顶点,也不要写成 C++ 的连续比较。
自测
输入:0 0 → 预期输出:yes
输入:1 -1 → 预期输出:yes
输入:2 0 → 预期输出:no
输入:0 -2 → 预期输出:no
自己试试:分别构造内部、边界、外部的点。
能否只改变一个坐标,让点从边界移到外部?
参考代码
1057:简单计算器¶
提示 1:这次做整数除法
输入7 2 /与2058的小数计算器有什么不同?
先读两个整数a、b,再读符号c。
提示 2:除法分支先看除数
这段只放在c == '/'的分支,其他运算分别处理;无效符号输出题目规定的文字。
提示 3:负商向零截断
先手算-7 / 2的C++结果,再运行检查。
不要为了让输出像数学小数而改成double。
常见错误
不要照搬 2058,把变量都改成 double,这样会改变本题的整数除法。负数时也不是向下取整,例如 -7 / 2 为 \(-3\)。
自测
输入:7 2 / → 预期输出:3
输入:-7 2 / → 预期输出:-3
输入:1 0 / → 预期输出:Divided by zero!
输入:1 0 % → 预期输出:Invalid operator!
输入:-10000 10000 * → 预期输出:-100000000
自己试试:自己选两个不能整除的整数,先写出商。
再只改变一个数的正负号,检查你是否分清了向零截断。
参考代码
// 如果if下面只有一句话,可以不用大括号,也可以提上来写
// 输出的话,一定要复制粘贴,千万不要自己敲,你看不清那些大小写和空格的
#include <iostream>
using namespace std;
int main() {
int a, b;
char c;
cin >> a >> b >> c;
if (c == '+') cout << a + b;
else if (c == '-') cout << a - b;
else if (c == '*') cout << a * b;
else if (c == '/') {
if (b == 0) cout << "Divided by zero!";
else cout << a / b;
} else cout << "Invalid operator!";
return 0;
}
1058:求一元二次方程¶
提示 1:先决定能不能开平方
读入double a, b, c,先求根号内的数:
用1 -3 2算得1,用1 0 1算得-4;负数时先走“没有实根”的分支。
提示 2:有实根时,先写一根
delta等于0时,两根相同;大于0时,分别计算两根。
把减号换成加号,补出另一根;分母的2 * a要一起括起来。
提示 3:最后才排顺序和输出
不要认定带减号的一根一定小,a为负时顺序可能反过来。
两根先算出来,比一下大小。
最后的等号、分号,还有五位小数,照题目写,别漏了。
知识补充
浮点计算可能有舍入误差,不能把任意一个固定的 eps 当成所有题目通用的“相等标准”。这里按题面给出的三种判别式情况实现;若只在非常接近重根时出错,把具体系数和算出的判别式一起检查。
常见错误
分母是整个 2 * a,必须写成 ... / (2 * a)。不要在判断判别式之前开平方,也不要自行为 \(a=0\) 设计一次方程分支,题面已经排除它。调试时先写出判别式和两根的手算值,再带着出错输入、实际输出找老师一起看。
自测
输入:1 -3 2 → 预期输出:x1=1.00000;x2=2.00000
输入:1 -2 1 → 预期输出:x1=x2=1.00000
输入:1 0 1 → 预期输出:No answer!
输入:-1 3 -2 → 预期输出:x1=1.00000;x2=2.00000
输入:-15.97 19.69 12.02 → 预期输出:x1=-0.44781;x2=1.68075
输入:1 0 0 → 预期输出:x1=x2=0.00000
自己试试:可以反过来造数据:先选两个整数根,再展开 \((x-r)(x-s)=0\) 得到系数。
试着让两根相等,或把三个系数同时乘以 \(-1\),预期结果应怎样变化?
参考代码
#include <iostream>
#include <cstdio>
#include <cmath>
#include <utility>
using namespace std;
int main() {
double a, b, c;
cin >> a >> b >> c;
double delta = b * b - 4 * a * c;
if (delta < 0) cout << "No answer!";
else if (delta == 0) {
double x = -b / (2 * a);
if (x == 0) x = 0;
printf("x1=x2=%.5lf", x);
} else {
double x1 = (-b - sqrt(delta)) / (2 * a);
double x2 = (-b + sqrt(delta)) / (2 * a);
if (x1 > x2) swap(x1, x2);
if (x1 == 0) x1 = 0;
if (x2 == 0) x2 = 0;
printf("x1=%.5lf;x2=%.5lf", x1, x2);
}
return 0;
}