跳转至

第二章 顺序结构程序设计

基础数学知识

  • 小数乘除法:会计算小数与整数、小数与小数的乘除法,理解除法结果可能带小数。对应 2066、1010、1013、1029 等题。
  • 分数运算、倒数和繁分数:知道分数表示除法,会读“分母里还有分数”的式子。例如 \(\frac{1}{\frac{1}{r_1}+\frac{1}{r_2}}\),要先算分母,再取倒数。对应 1010、1015。
  • 百分数与折扣:知道 \(20\%=0.2\),八折表示按原价的 \(80\%\) 计算,能把人数比例写成百分数。对应 2066、1011、1414。
  • 用字母表示数、代入公式和列方程:能把已知数代入含字母的式子;理解鸡兔同笼里“头数不变、腿数变化”的数量关系。对应 2068、1012、1013 等题。鸡兔同笼也可以用假设法,不一定要先学方程。
  • 负数的运算、数轴与绝对值:知道负数在零的哪一边,理解“向零靠近”和“向更小的数靠近”不同;绝对值可以理解为到零的距离。对应 1019、1029,以及 1034 的坐标公式。
  • 平方、立方与乘方:\(r^2=r\times r\),\(r^3=r\times r\times r\),\(2^n\) 表示 \(n\) 个 2 相乘(\(n\) 为正整数),并且 \(2^0=1\)。对应 1012、1030、1037 等题。
  • 平方根与开平方:知道 \(\sqrt{25}=5\),也就是“哪个非负数的平方等于 25”。对应 2073、1033、1034;代码里用 sqrt 计算,不用先练手算开平方。
  • 圆的周长和面积:理解半径、直径与圆周率,会用 \(d=2r\)、\(C=2\pi r\)、\(S=\pi r^2\)。对应 2067、1014。圆周率按各题给出的值使用。
  • 圆柱、球的体积与容积单位:圆柱体积 \(V=\pi r^2h\),球体积 \(V=\frac{4}{3}\pi r^3\);知道 \(1\) 升等于 \(1000\) 立方厘米。对应 1030、1032。
  • 加权平均与总分的还原:人数或权重不同时,不能直接把几个平均分再平均;先求总量,再除以总份数。也要会用“平均分乘人数”还原总分。对应 1414、2071、2072。
  • 等差数列:相邻两项的差相同,这个差叫公差。会用 \(d=a_2-a_1\)、\(a_n=a_1+(n-1)d\) 求第 \(n\) 项。对应 1035。
  • 平面直角坐标与勾股定理:用一对坐标表示点的位置,利用两条直角边求斜边;两点距离为 \(\sqrt{(x_a-x_b)^2+(y_a-y_b)^2}\)。对应 1033、1034。
  • 海伦公式:知道三角形三边时,先求半周长 \(p=\frac{a+b+c}{2}\),再求面积 \(S=\sqrt{p(p-a)(p-b)(p-c)}\)。对应 2073、1034,先会代入就可以。
  • 科学计数法:理解 \(1.25\times 10^1=12.5\),能看懂程序输出中的 1.250000e+01。对应 1027。
  • 向上取整、向下取整与向零截断:知道“不满一桶仍要多提一桶”“咬过的苹果不能算完整”;也要分清负数时三种取整方式的区别。对应 1008、1019、1029、1032、1038。

第一节 运算符和表达式

2064:【例 2.1】交换值

提示 1:先找一个地方保管旧值

a = 3、b = 8,想把两者交换。

这里别急着写a = b,原来的3会丢。

先找个临时变量t,把3存一下。

提示 2:剩下的那一步你来补
int t = a;
a = b;

此时a是8,t是3;再补一句,让b拿到原来的3。

先把方法一写熟,后面两种,再比较着看。

常见错误

a = b; b = a; 不能完成交换。第一句执行完,原来的 a 就丢了。你可以把每一步的变量值写下来,看看两者最后是不是变成了一样的数。

自测

输入:3 8 → 预期输出:8 3

再试两个数相等的情况。

方法一:临时变量
#include <iostream>

using namespace std;

int main() {
    int a, b;
    cin >> a >> b;

    int t = a;
    a = b;
    b = t;

    cout << a << ' ' << b;

    return 0;
}
方法二:swap函数(拓展)
#include <iostream>
#include <utility>

using namespace std;

int main() {
    int a, b;
    cin >> a >> b;

    swap(a, b);

    cout << a << ' ' << b;

    return 0;
}
方法三:倒序输出(变量未交换)
#include <iostream>

using namespace std;

int main() {
    int a, b;
    cin >> a >> b;

    cout << b << ' ' << a;

    return 0;
}

2065:【例 2.2】整数的和

提示 1:三个数都要读进来

先在模板里定义三个整数变量,按顺序读入a、b、c。

int sum = a + b + c;

这句放在读入前还是后?接着只输出sum。

常见错误

cin 后面用 >>,cout 后面用 <<。读入三个数,就要写三次 >>;提交时只输出答案,不加“请输入”之类的话。

自测

输入:1 2 3 → 预期输出:6

再试包含零和负数的情况。

参考代码
#include <iostream>

using namespace std;

int main() {
    int a, b, c;
    cin >> a >> b >> c;

    cout << a + b + c;

    return 0;
}

2066:【例 2.3】买图书

提示 1:八折是实际付多少

原价10元,打八折付8元,这个先算明白。

再看输入,手里的钱、书的原价,分别用哪个变量?

提示 2:先算花费,再算剩余

n是原有的钱,m是原价:

double left = n - m * 0.8;

别用整数除法8 / 10代替0.8;把结果按两位小数输出。

常见错误

8 / 10 是整数除法,结果为零,不能拿它代替 0.8。算出来的钱可能带小数,不要先存进 int 再输出。

自测

输入:10 3 → 预期输出:7.60

参考代码
#include <iostream>
#include <cstdio>

using namespace std;

int main() {
    int n, m;
    cin >> n >> m;

    printf("%.2lf", n - m * 0.8);

    return 0;
}

1006:A+B 问题

提示 1:把熟悉的加法独立写一次

先合上代码,自己读入两个整数a、b。

cout << a << b;和输出a + b,用2 5试时有什么不同?

常见错误

cout << a << b; 是把两个数接着输出,不是相加。要计算和,输出的应是 a + b。

自测

输入:2 5 → 预期输出:7

输入:-2 2 → 预期输出:0

参考代码
#include <iostream>

using namespace std;

int main() {
    int a, b;
    cin >> a >> b;

    cout << a + b;

    return 0;
}

1007:计算 (a+b)×c 的值

提示 1:先算哪一层括号

用2 3 4手算,先加再乘应得到20,先乘会得到多少?

提示 2:把先算的部分括起来

已读入a、b、c:

cout << (a + b) * c;

检查乘号写成*,不要照搬数学里的×。

常见错误

a + b * c 会先算乘法。纸上有括号,代码里也要有;数学中的乘号在 C++ 里写成 *。

自测

输入:2 3 4 → 预期输出:20

参考代码
#include <iostream>

using namespace std;

int main() {
    int a, b, c;
    cin >> a >> b >> c;

    cout << (a + b) * c;

    return 0;
}

1008:计算 (a+b)/c 的值

提示 1:这次保留整数商

对2 5 3,先算7除以3,程序的整数结果是什么?

提示 2:整数两边相除会截去小数
cout << (a + b) / c;

读入三个整数后再执行;负数除不尽时向零截断,别改成浮点除法。

常见错误

这里要的是整数除法的结果,不要改成小数输出。向零截断和向下取整,在负数时可能不同;a + b 外面的括号也不能省。

自测

输入:2 5 3 → 预期输出:2

再观察负数做整数除法时,向零截断与向下取整的区别。

参考代码
#include <iostream>

using namespace std;

int main() {
    int a, b, c;
    cin >> a >> b >> c;

    cout << (a + b) / c;

    return 0;
}

1009:带余除法

提示 1:商和余数各算一次

17除以5,商和余数分别是多少?

读入整数a、b,分别用a / b和a % b。

提示 2:输出两项,检查一条关系
cout << a / b << ' ' << a % b;

用“商乘除数加余数”等于被除数,检查自己的结果。

常见错误

/ 求商,% 求余,别用反了。% 不能用来计算浮点数的余数;输出两个结果时,中间要有空格。

自测

输入:17 5 → 预期输出:3 2

再试可以整除的情况,余数应为零。

参考代码
#include <iostream>

using namespace std;

int main() {
    int a, b;
    cin >> a >> b;

    cout << a / b << ' ' << a % b;

    return 0;
}

1010:计算分数的浮点数值

提示 1:别让小数在除法时丢掉

输入1和2时,整数表达式a / b先算出0,之后存到double还能变回0.5吗?

提示 2:在除之前改变计算类型
double x = 1.0 * a / b;

两个整数已经读进来了,再用%.9lf输出。

回头和1008比一下,同样是除法,这里哪儿变了?

常见错误

double x = a / b; 仍然先做整数除法,算完才存入 double。(double)(a / b) 也转晚了,要在除之前转换其中一个数。

自测

输入:1 2 → 预期输出:0.500000000

再试不能整除的分数。

方法一:浮点运算与 printf 输出
#include <iostream>
#include <cstdio>

using namespace std;

int main() {
    int a, b;
    cin >> a >> b;

    printf("%.9lf", 1.0 * a / b);

    return 0;
}
方法二:类型转换与 cout 输出
#include <iostream>
#include <iomanip>

using namespace std;

int main() {
    int a, b;
    cin >> a >> b;

    cout << fixed << setprecision(9)
         << (double)a / b;

    return 0;
}

第二节 常量和变量

2067:【例 2.5】圆

提示 1:同一个半径算三件事

先在纸上写直径、周长、面积的公式,输入半径1时各是多少?

提示 2:平方用两次相乘

半径r用double,圆周率按题面取3.14159。

double s = 3.14159 * r * r;

自己补直径和周长,按题面顺序输出,每项四位小数。

常见错误

输入的是半径,别当成直径。r ^ 2 不是平方,要写 r * r;圆周率按题目给的值用,别自己换。

自测

输入:1 → 预期输出:2.0000 6.2832 3.1416

参考代码
#include <iostream>
#include <cstdio>

using namespace std;

int main() {
    double r;
    cin >> r;

    printf("%.4lf %.4lf %.4lf", 2*r, 2*3.14159*r, 3.14159*r*r);

    return 0;
}

2068:【例 2.6】鸡兔同笼

提示 1:先把所有头都看成鸡

10个头若全是鸡,有20条腿;实际28条,多了多少?

一只鸡换成一只兔,就多2条腿。

这么换,要换几只?

提示 2:先求兔,再求鸡

头数x、腿数y已读入:

int tuzi = (y - x * 2) / 2;

鸡数用总头数减兔数;算完代回头数和腿数检查,输出鸡在前。

常见错误

输出顺序是鸡在前,兔在后。算完以后,把数量代回头数、腿数两个条件,自己核对一遍。本题按合法数据求解,自己编数据时也要让头数和腿数对得上。

自测

输入:10 28 → 预期输出:6 4

再试全是鸡、全是兔的情况。

方法一:公式计算(重点)
// 不用循环
// 前提是,你已经推完了式子
#include <iostream>

using namespace std;

int main() {
    int x, y;
    cin >> x >> y;

    int tuzi = (y - x * 2) / 2;
    int ji = x - tuzi;

    cout << ji << ' ' << tuzi;

    return 0;
}
方法二:单层枚举(拓展)
// 一层循环
// j其实是不用枚举的
#include <iostream>

using namespace std;

int main() {
    int x, y;
    cin >> x >> y;

    for (int i = 0; i <= x; i++) {
        int j = x - i;
        if (2 * i + 4 * j == y) cout << i << ' ' << j;
    }

    return 0;
}
方法三:双层枚举(拓展)
// 两层循环
#include <iostream>
#include <algorithm>

using namespace std;

int main() {
    int x, y;
    cin >> x >> y;

    for (int i = 0; i <= x; i++)
        for (int j = 0; j <= min(x, y / 4); j++)
            if (i + j == x && 2 * i + 4 * j == y)
                cout << i << ' ' << j;

    return 0;
}

1011:甲流疫情死亡率

提示 1:分子分母先摆对

1000人中死亡5人,死亡人数应在分子还是分母?

提示 2:算的是百分号前的数

a为确诊人数、b为死亡人数:

printf("%.3lf%%", 1.0 * b / a * 100);

%%显示一个百分号;去掉1.0会在哪一步丢失小数?

常见错误

先检查分子分母,再检查有没有乘 100。printf 里要输出一个百分号,需要写 %%;除法之前也要先转成浮点计算。

自测

输入:1000 5 → 预期输出:0.500%

参考代码
#include <iostream>
#include <cstdio>

using namespace std;

int main() {
    int a, b;
    cin >> a >> b;

    printf("%.3lf%%", 1.0 * b / a * 100);

    return 0;
}

1012:计算多项式的值

提示 1:先拿2试一下

先只取x = 2,\(x^3\)就是\(2\times2\times2\)。

把式子写成\(a\times8+b\times4+c\times2+d\),再对照输入的五个量。

提示 2:照着乘法式写代码

五个量都已用double读入:

double ans = a * x * x * x + b * x * x + c * x + d;

在四项之间画分界,检查每项用了几次x,再补七位小数输出。

常见错误

C++ 中 ^ 是按位异或,不是乘方。输入先读 x,再读四个系数;如果答案不对,先逐项输出检查,找出从哪一项开始算错。查完后,记得删掉临时输出再提交。

自测

输入:2 1 2 3 4 → 预期输出:26.0000000

方法一:直接计算
#include <iostream>
#include <cstdio>

using namespace std;

int main() {
    double x, a, b, c, d;
    cin >> x >> a >> b >> c >> d;
    double ans = a*x*x*x + b*x*x + c*x + d;

    printf("%.7f", ans);

    return 0;
}
方法二:逐层提取公因式(秦九韶算法)
#include <iostream>
#include <cstdio>

using namespace std;

int main() {
    double x, a, b, c, d;
    cin >> x >> a >> b >> c >> d;
    double ans = ((a * x + b) * x + c) * x + d;

    printf("%.7f", ans);

    return 0;
}

1013:温度表达转化

提示 1:先用冰点验公式

华氏32度对应摄氏0度,公式中哪一部分会变成0?

提示 2:先减32,再换比例

读入double F后:

double c = 5 * (F - 32) / 9;

为什么括号不能丢?用212再验一次,然后保留五位小数。

常见错误

5 / 9 * (F - 32) 会先把 5 / 9 算成零。可以写 5.0 / 9 * (F - 32),也可以像参考代码那样先乘后除;F - 32 的括号要保留。

自测

输入:32 → 预期输出:0.00000

输入:212 → 预期输出:100.00000

参考代码
#include <iostream>
#include <cstdio>

using namespace std;

int main() {
    double F;
    scanf("%lf", &F);

    printf("%.5lf", 5 * (F - 32) / 9);

    return 0;
}

1014:与圆相关的计算

提示 1:先写三个结果的顺序

直径、周长、面积分别需要几次乘半径?

半径为1时先手算,和2067对照。

提示 2:算式和格式分开检查

圆周率为3.14159,半径r用double:

double c = 2 * 3.14159 * r;

自己补另外两项,三项都保留四位小数,用空格分开。

常见错误

不同题目指定的圆周率可能不同,还是要重新看一眼。scanf 读 double 用 %lf,后面要写变量地址 &r。

自测

输入:1 → 预期输出:2.0000 6.2832 3.1416

方法一:直接代入公式
#include <iostream>
#include <cstdio>

using namespace std;

int main() {
    double r;
    cin >> r;

    printf("%.4lf %.4lf %.4lf", 2*r, 2*3.14159*r, 3.14159*r*r);

    return 0;
}
方法二:使用常量与 scanf 输入
// 这题用 cin 或 scanf 都可以,先用自己熟悉的写法
// 换成 scanf 时,注意格式符和变量地址
#include <iostream>
#include <cstdio>

using namespace std;

const double PII = 3.14159;

int main() {
    double r;
    scanf("%lf", &r);

    printf("%.4lf %.4lf %.4lf", 2 * r, 2 * PII * r, PII * r * r);

    return 0;
}

1015:计算并联电阻的阻值

提示 1:两个相同电阻试公式

两个10欧姆并联,结果应为5,而不是20。

先算两个倒数的和,再取倒数。

提示 2:不要算成整数倒数

r1、r2都已读为double:

double r = 1.0 / (1 / r1 + 1 / r2);

为什么最外层的分母需要括号?自己补两位小数输出。

常见错误

分母是一整个和,外层括号不能少。两个电阻都为正数时,并联结果应比任意一个原电阻都小,可以用这个办法先判断结果是否合理。

自测

输入:10 10 → 预期输出:5.00

输入:3 6 → 预期输出:2.00

方法一:按原公式计算
#include <iostream>
#include <cstdio>

using namespace std;

int main() {
    double r1, r2;
    scanf("%lf%lf", &r1, &r2);

    double r = 1.0 / (1 / r1 + 1 / r2);

    printf("%.2lf", r);

    return 0;
}
方法二:通分后计算
#include <iostream>
#include <cstdio>

using namespace std;

int main() {
    double r1, r2;
    cin >> r1 >> r2;
    double r = r1 * r2 / (r1 + r2);

    printf("%.2f", r);

    return 0;
}

第三节 标准数据类型

1414:【17NOIP 普及组】成绩

提示 1:三科不是各占三分之一

分数a、b、c分别占20%、30%、50%,先把三科都取100检查公式。

提示 2:合并到同一个除法
int sum = a * 2 + b * 3 + c * 5;

最后除以10再输出;为什么先算20 / 100会出问题?

常见错误

这里是按比例加权,不能直接把三科相加再除以 3。20 / 100 会得到零;参考代码能用整数除法,是因为本题保证各科成绩都是 10 的倍数。

自测

输入:100 100 100 → 预期输出:100

输入:80 90 100 → 预期输出:93

参考代码
#include <iostream>

using namespace std;

int main() {
    int a, b, c;
    cin >> a >> b >> c;

    cout << (a * 2 + b * 3 + c * 5) / 10;

    return 0;
}

1016:整型数据类型存储空间大小

提示 1:查询类型,不是猜答案

int、short占多少字节,先别背一个数。

写出来,让你自己的电脑算一下。

提示 2:先查一种类型
cout << sizeof(int);

仿照查询short,两项中间加空格;不要把某台电脑的结果写成固定答案。

常见错误

sizeof 的单位是字节,不是位。查询类型时写 sizeof(int),不能写 sizeof int;查询变量时,两种括号写法都可以。

自测

比较sizeof(int)和sizeof(a)(a为int变量),结果应相同。

方法一:查询变量大小
#include <iostream>

using namespace std;

int main() {
    int a;
    short b;

    cout << sizeof a << ' ' << sizeof b;

    return 0;
}
方法二:查询类型大小
// sizeof 后面直接接变量类型,要加上括号
// 后面接变量名,则是可加可不加
#include <iostream>

using namespace std;

int main() {

    cout << sizeof(int) << ' ' << sizeof(short);

    return 0;
}

1017:浮点型数据类型存储空间大小

提示 1:float和double分别查询

两个都是浮点类型,存储空间一定相同吗?

提示 2:用sizeof问编译器
cout << sizeof(float);

自己补double及分隔空格;这里查询类型大小,不需要给变量读入小数。

常见错误

不要把记住的字节数直接输出,让 sizeof 去查。变量里存了几位小数,不会改变这个类型占用的空间。

自测

float a;:比较sizeof(float)和sizeof(a),结果应相同。

double b;:比较sizeof(double)和sizeof(b),结果应相同。

参考代码
#include <iostream>

using namespace std;

int main() {
    float a;
    double b;

    cout << sizeof(a) << ' ' << sizeof(b);

    return 0;
}

1018:其他数据类型存储空间大小

提示 1:大小不是变量当前的值

bool可以存真假,char可以存字符;题目要的是值,还是各占几字节?

提示 2:类型名可以直接查询
cout << sizeof(bool);

仿照它输出char的大小,两个结果之间留一个空格。

常见错误

逻辑上只有两种值,不等于实际只占一位。sizeof(char) 按 C++ 定义是一个字节;bool 的大小则让程序查询。

自测

bool a = true;:记录sizeof(a),再令a = false;,两次结果应相同。

参考代码
#include <iostream>

using namespace std;

int main() {
    bool a;
    char b;

    cout << sizeof(a) << ' ' << sizeof(b);

    return 0;
}

1019:浮点数向零舍入

提示 1:向零不是四舍五入

3.9变成3,-3.9会变成-3还是-4?

在数轴上朝0的方向看。

提示 2:转换时去掉小数部分

读入double x后:

cout << (int)x;

用0.9和-0.9再试;这一步也不是对负数向下取整。

知识补充:强制类型转换

(int)x得到一个整数结果,不会改变x本身。

double x = -3.9;
int a = (int)x;  // a为-3,x仍为-3.9

转换后的整数必须能放进int,这不是处理任意大浮点数的方法。

常见错误

不要用四舍五入;floor(-3.9) 的结果为 \(-4\),与本题要求不同;printf("%.0f", x) 是按输出精度舍入,不能代替向零截断。

自测

输入:3.9 → 预期输出:3

输入:-3.9 → 预期输出:-3

输入:0.9 → 预期输出:0

输入:-0.9 → 预期输出:0

参考代码
#include <iostream>

using namespace std;

int main() {
    double x;
    cin >> x;

    cout << (int)x;

    return 0;
}

1020:打印 ASCII 码

提示 1:字符和编码是两种输出

读入A后,直接输出字符会看到A,题目却要求65。

提示 2:先把类型转成整数
cout << (int)c;

c已用char读入;换成字符0时,为什么结果不是整数0?

常见错误

cout << c; 输出字符,cout << 'c'; 输出字母 c,都不是我们要的编码。也不用减去 '0',那是在处理数字字符时的另一种操作。

自测

输入:A → 预期输出:65

输入:0 → 预期输出:48

参考代码
#include <iostream>

using namespace std;

int main() {
    char c;
    cin >> c;

    cout << (int)c;

    return 0;
}

1021:打印字符

提示 1:和上一题反过来

输入65,要看到字符A,不是数字65。

提示 2:让整数按字符输出

整数x已读入:

cout << (char)x;

再用48试一次,检查自己是否把转换方向写反。

常见错误

cout << x; 输出的还是整数编码,需要先转换成 char。变量名放进引号里,就变成了固定的文字。

自测

输入:65 → 预期输出:A

输入:48 → 预期输出:0

参考代码
#include <iostream>

using namespace std;

int main() {
    int x;
    cin >> x;

    cout << (char)x;

    return 0;
}

1022:整型与布尔型的转换

提示 1:先预测三个输入

0、7、-7各转成bool后是什么?

非零对应true,不只正数会变成真。

提示 2:让赋值完成两次转换

整数a已读入:

bool b = a;
a = b;

最后输出a,真转回整数是多少?

常见错误

负数也是非零数,也会转成 true。默认输出 bool 会显示 0 或 1;开了 boolalpha 就会变成单词,本题不要这样输出。

自测

输入:0 → 预期输出:0

输入:7 → 预期输出:1

输入:-7 → 预期输出:1

参考代码
#include <iostream>

using namespace std;

int main() {
    int a;
    cin >> a;

    bool b;
    b = a;

    a = b;

    cout << a;

    return 0;
}

1023:Hello,World! 的大小

提示 1:文字后面还藏着结束标记

先查询sizeof("A"),为什么得到2而不是1?

字符串字面量末尾还有一个'\0'。

提示 2:逐个数原文,再加一
cout << sizeof("Hello, World!");

这里有逗号和空格,两者都要计入;不要和不带空格的1001混淆。

常见错误

逗号后面的空格也要算,英文标点不能换成中文标点。sizeof 算这里的字符数组所占空间,包含结束符;平时说的字符串长度不包含这个结束符。

自测

sizeof("A") → 预期结果:2。

sizeof("AB") → 预期结果:3。

参考代码
#include <iostream>

using namespace std;

int main() {

    cout << sizeof("Hello, World!");

    return 0;
}

第四节 数据输入输出

2069:【例 2.12】糖果游戏

提示 1:后分的人已经收到糖

先只算a分糖这一步,分给左右邻居的糖会影响后面的人吗?

顺序是a、b、c、d、e,不能同时按最初数量计算。

提示 2:用临时变量保住一份

当前糖数在a,每份先用整数除法算出:

t = a / 3;
e += t;
b += t;
a = t;

仿照完成其余四个人,注意邻居是谁;最后按每项五列输出。

常见错误

这题是依次分,不是五人同时分。每轮先算好一份有几颗,再更新三个人的糖数;%5d 表示每项至少占五列,不能只用一个空格代替。

自测

输入:8 9 10 11 12 → 预期输出: 11 7 9 11 6

按每项5列输出,保留前导空格。

核对分糖过程

先手算,再找出从哪一轮开始不同。

             a   b   c   d   e
开始         8   9  10  11  12
a 分完       2  11  10  11  14
b 分完       5   3  13  11  14
c 分完       5   7   4  15  14
d 分完       5   7   9   5  19
e 分完      11   7   9  11   6
方法一:五个变量依次模拟
#include <iostream>
#include <cstdio>

using namespace std;

int t;

int main()
{
    int a, b, c, d, e;
    cin >> a >> b >> c >> d >> e;

    t = a / 3;
    e += t, b += t;
    a = t;

    t = b / 3;
    a += t, c += t;
    b = t;

    t = c / 3;
    b += t, d += t;
    c = t;

    t = d / 3;
    c += t, e += t;
    d = t;

    t = e / 3;
    a += t, d += t;
    e = t;

    printf("%5d%5d%5d%5d%5d", a, b, c, d, e);

    return 0;
}
方法二:数组与循环(拓展)
#include <iostream>
#include <cstdio>

using namespace std;

int a[10];

int main() {
    for (int i = 0; i < 5; i++) cin >> a[i];

    for (int i = 0; i < 5; i++) {
        int t = a[i] / 3;
        int l = (i - 1 + 5) % 5, r = (i + 1) % 5;
        a[l] += t, a[r] += t, a[i] = t;
    }

    for (int i = 0; i < 5; i++) printf("%5d", a[i]);

    return 0;
}

1024:保留 3 位小数的浮点数

提示 1:位数不够也要补零

输入1.2,保留三位小数时应显示什么?

提示 2:读入格式与输出格式分清

变量a是float,先用%f读入:

printf("%.3f", a);

再换一个需要舍入的数检查,别靠乘1000再截断代替格式化。

常见错误

scanf 读 float 用 %f,读 double 才用 %lf。setprecision(3) 要和 fixed 一起用,才表示小数点后保留三位。

自测

输入:1.2 → 预期输出:1.200

再试需要舍入的小数。

方法一:printf 输出
#include <cstdio>

using namespace std;

int main() {
    float a;
    scanf("%f", &a);

    printf("%.3f", a);

    return 0;
}
方法二:cout 输出
#include <iostream>
#include <iomanip>

using namespace std;

int main() {
    float x;
    cin >> x;

    cout << fixed << setprecision(3) << x;

    return 0;
}

1025:保留 12 位小数的浮点数

提示 1:十二位小数需要哪种类型

定义double a,输入1.25时,后面应补几个零?

提示 2:用输出格式控制位数
printf("%.12lf", a);

前面自己补scanf("%lf", &a);,别把double的读入格式写成%f。

常见错误

float 通常只有约 6~7 位十进制有效数字,double 通常约 15~16 位。这里说的是有效数字,不是小数位数;换个输出格式,不能把已经丢掉的精度补回来。

自测

输入:1.25 → 预期输出:1.250000000000

参考代码
#include <cstdio>

using namespace std;

int main() {
    double a;
    scanf("%lf", &a);

    printf("%.12lf", a);

    return 0;
}

1026:空格分隔输出

提示 1:四个量按类型读入

先标出字符、整数、float、double分别放入a、b、c、d。

提示 2:格式符一一对应
printf("%c %d %f %lf", a, b, c, d);

格式串中的空格负责分隔;检查变量顺序与读入顺序是否一致。

常见错误

普通 cout 的默认格式,和 printf 的 %f 不一样。若换成 scanf 读字符,%c 不会自动跳过空白;需要时在它前面加一个空格。

自测

输入:a 1 2.5 3.5 → 预期输出:a 1 2.500000 3.500000

参考代码
#include <iostream>
#include <cstdio>

using namespace std;

int main() {
    char a;
    int b;
    float c;
    double d;

    cin >> a >> b >> c >> d;

    printf("%c %d %f %lf", a, b, c, d);

    return 0;
}

1027:输出浮点数

提示 1:先完成自己熟悉的两行

同一个数先按普通小数、五位小数各输出一行。

读入double a后:

printf("%lf\n%.5lf\n", a, a);
提示 2:再接上两种新格式

%e输出科学计数法,例如12.5显示为1.250000e+01。

%g按数值选择小数或科学计数法,并去掉小数部分末尾不需要的零。

接上第三、四行;每个格式符都要有一个对应的a。

常见错误

四种结果要分成四行。%g 不是简单地挑较短的字符串;同样别忘了,scanf 读 double 仍然用 %lf。

自测

输入:12.5

预期输出:

12.500000
12.50000
1.250000e+01
12.5
参考代码
// %f:小数形式;%e:科学计数法
// %g:按指数规则选择形式,默认保留 6 位有效数字
#include <cstdio>

using namespace std;

int main() {
    double a;
    scanf("%lf", &a);

    printf("%lf\n%.5lf\n%e\n%g", a, a, a, a);

    return 0;
}

1028:字符菱形

提示 1:先描出五行的宽度

用自测的&画菱形,先数每行空格,再数字符。

前导空格为2、1、0、1、2,对应字符数是多少?

提示 2:先写上半边,再对称补下半边

字符已读入c:

printf("  %c\n", c);
printf(" %c%c%c\n", c, c, c);

自己写中间一行,下面两行与哪两行相同?最后一行可以不换行。

常见错误

前导空格决定图形是否对齐,用空格,不用 Tab。cout << 'c'; 会输出字母 c,而 cout << c; 才是输出变量里存的字符。

自测

输入:

&

预期输出:

  &
 &&&
&&&&&
 &&&
  &

再把输入换成井号,观察五行是否仍然对称。

参考代码
#include <iostream>
#include <cstdio>

using namespace std;

int main() {
    char c;
    cin >> c;

    printf("  %c\n", c);
    printf(" %c%c%c\n", c, c, c);
    printf("%c%c%c%c%c\n", c, c, c, c, c);
    printf(" %c%c%c\n", c, c, c);
    printf("  %c", c);

    return 0;
}

第五节 顺序结构实例

2070:【例 2.13】数字对调

提示 1:先拆个位、十位、百位

123对调后是321;120对调后输出的是整数21。

三个数字先拆出来,写到纸上。

最后的算式,等会儿再拼。

提示 2:按新位置乘权重

整数x已读入:

int ge = x % 10;
int shi = x / 10 % 10;
int bai = x / 100;

个位移到百位乘100,十位乘10,再加原来的百位;自己补最后的算式。

常见错误

这题是重新组成一个整数,前面的零不会显示。1031 要逐位输出,零要保留,两道题做完可以放在一起比一比。

自测

输入:123 → 预期输出:321

输入:120 → 预期输出:21

参考代码
#include <iostream>

using namespace std;

int main() {
    int x;
    cin >> x;

    cout << x % 10 * 100 + (x / 10) % 10 * 10 + x / 100;

    return 0;
}

2071:【例 2.14】平均分

提示 1:两个班人数不同时不能直接平均

一个班平均87、另一个85;若第一个班只有1人、另一个3人,总分各是多少?

提示 2:用总分除以总人数

人数a、b已读入:

double ave = 1.0 * (a * 87 + b * 85) / (a + b);

为什么分母不是2?按四位小数输出。

常见错误

(87 + 85) / 2 没有考虑两个班的人数。先求两个班的总分,再除以总人数,分母的括号也要写好。

自测

输入:1 3 → 预期输出:85.5000

再试两班人数相等的情况。

参考代码
#include <iostream>
#include <cstdio>

using namespace std;

int main() {
    int a, b;
    cin >> a >> b;

    printf("%.4lf", 1.0*(a*87+b*85)/(a+b));

    return 0;
}

2072:【例 2.15】歌手大奖赛

提示 1:先由平均分还原总分

6人的平均分9.6,总分是多少?

去掉最高分后5人的平均分9.4,两个总分之差是谁的分数?

提示 2:最高最低分别作差
double maxn = 9.6 * 6 - 9.4 * 5;

照这个办法用9.8求最低分,再从原总分减去两者;此时还剩几人?

常见错误

去掉一位评委的分数,就只剩五个分数;去掉最高、最低后,就剩四个分数。人数要跟着变。这题没有输入,不需要写 cin。

自测

无需输入,预期输出: 9.60(宽度为5列)。

手算核对:最高分10.6,最低分8.6。

参考代码
// 复述题意
// 解题思路
// 先求出最高分
// 求出最低分
// 去掉最高最低,求平均分

#include <cstdio>

using namespace std;

int main() {
    double maxn = 9.6 * 6 - 9.4 * 5;
    double minn = 9.6 * 6 - 9.8 * 5;
    double ave = (9.6 * 6 - maxn - minn) / 4;

    printf("%5.2lf", ave);

    return 0;
}

2073:【例 2.16】三角形面积

提示 1:先求半周长

三边3、4、5的周长是12,半周长是多少?

读入double a, b, c,先算p = (a + b + c) / 2。

提示 2:把根号内四项写完整
double s = sqrt(p * (p - a) * (p - b) * (p - c));

使用<cmath>,每个差都要有括号;再补三位小数输出。

常见错误

p 是半周长,不是周长;三条边和面积都用 double。sqrt 需要 <cmath>,根号里面的几个差值分别加括号,再相乘。

自测

输入:3 4 5 → 预期输出:6.000

参考代码
#include <iostream>
#include <cstdio>
#include <cmath>

using namespace std;

int main() {
    double a, b, c;
    cin >> a >> b >> c;

    double p = (a + b + c) / 2;
    double s = sqrt(p * (p - a) * (p - b) * (p - c));

    printf("%.3lf", s);

    return 0;
}

1029:计算浮点数相除的余

提示 1:想象先减去整份除数

5.5里能减去几个完整的2,还剩多少?

本题非负被除数、正除数,先求整数份数。

提示 2:余数从原数中减出来

a、b用double读入:

double k = floor(a / b);
double r = a - b * k;

浮点数不能直接用%;自己用4和2检查余数是否为0。

常见错误

% 不能用于 double,也不能先把两个输入都截成整数。如果只差一点点,先把中间结果多输出几位看看,别上来就随意加减一个小数。临时检查用的输出,提交前要删掉。

自测

输入:5.5 2 → 预期输出:1.5

输入:4 2 → 预期输出:0

参考代码
#include <iostream>
#include <cmath>

using namespace std;

int main() {
    double a, b;
    cin >> a >> b;

    double k = floor(a / b);
    double r = a - b * k;

    cout << r;

    return 0;
}

1030:计算球的体积

提示 1:先检查四分之三有没有写反

球体积为\(\frac43\pi r^3\),半径要乘几次?

圆周率按题面取3.14。

提示 2:让四除以三保留小数
double volume = 4.0 / 3 * 3.14 * r * r * r;

r已读入;若改成4 / 3,在哪一步失去小数?

常见错误

4 / 3 得到整数 1,要写成 4.0 / 3。立方是 r * r * r,不是 r ^ 3;公式里的半径要乘三次。

自测

输入:4 → 预期输出:267.95

输入:0 → 预期输出:0.00

参考代码
#include <iostream>
#include <cstdio>

using namespace std;

int main() {
    double r;
    cin >> r;
    const double PI = 3.14;
    double volume = 4.0 / 3 * PI * r * r * r;

    printf("%.2f", volume);

    return 0;
}

1031:反向输出一个三位数

提示 1:反向的是数字显示顺序

120要求显示021,和2070把结果当整数计算有什么区别?

提示 2:拆开后直接依次输出
cout << x % 10;

x已读入,接着输出十位、百位,中间不要空格,也不要重新合成整数。

常见错误

先合成整数再输出,前导零就没了。这里把个位、十位、百位分别连续输出,中间不要加空格。

自测

输入:120 → 预期输出:021

输入:100 → 预期输出:001

方法一:逐位取出并输出
// 这个用位置制思想,拿到个十百位上的数字,依次输出
#include <iostream>

using namespace std;

int main() {
    int x;
    cin >> x;

    cout << x % 10 << x / 10 % 10 << x / 100;

    return 0;
}
方法二:限定输入宽度

%1d 中的 1 限制这次转换最多读取一个字符。本题输入是一个正的三位数,连续读三次,就分别读到了百位、十位、个位。

这和输出时的宽度不是一回事,先在这道题里看懂它怎么用就可以。

// 注意看 scanf:%1d 最多读取一位数字,这里分别读出百位、十位、个位
#include <iostream>
#include <cstdio>

using namespace std;

int main() {
    int a, b, c;
    scanf("%1d%1d%1d", &a, &b, &c);

    cout << c << b << a;

    return 0;
}
方法三:字符串反转(拓展)
// 当你学了string的时候,就是字符串
// 你就可以把这个三位数,当成一个字符串处理
// 方法又多了一种
#include <iostream>
#include <algorithm>
#include <string>

using namespace std;

int main() {
    string s;
    cin >> s;

    reverse(s.begin(), s.end());

    cout << s;

    return 0;
}

1032:大象喝水查

提示 1:先统一到立方厘米

20升是20000立方厘米,一桶水的体积怎么求?

输入顺序是高h、半径r,圆周率取3.14。

提示 2:不满一桶也要再喝一桶

已算出每桶体积volume:

cout << (int)ceil(20000.0 / volume);

用<cmath>中的ceil向上取整,直接转成int会少算哪种情况?

常见错误

需要两桶多,截断后只剩两桶,当然不够。但恰好是两桶时,又不能多加一桶。用 ceil 向上取整,单位也要统一成同一种。

自测

输入:23 11 → 预期输出:3

另外思考:恰好装满整数桶与最后一桶未装满时,应怎样取整?

参考代码
#include <iostream>
#include <cmath>

using namespace std;

int main() {
    double h, r;
    cin >> h >> r;
    const double PI = 3.14;
    double volume = PI * r * r * h;

    cout << (int)ceil(20000.0 / volume);

    return 0;
}

1033:计算线段长度

提示 1:把斜线拆成横竖差

两点横向差3、纵向差4,线段应长5;先画这个直角三角形。

提示 2:横差平方加竖差平方

四坐标已读入xa、ya、xb、yb:

double dx = xa - xb, dy = ya - yb;

用sqrt(dx * dx + dy * dy)求长度;差可能为负,平方后还有负号吗?

常见错误

坐标可能带小数,要用 double。先分别作差,再平方、求和、开根号;不要把两个坐标差直接相加。

自测

输入:0 0 3 4 → 预期输出:5.000

再试两点重合的情况。

参考代码
#include <iostream>
#include <cstdio>
#include <cmath>

using namespace std;

int main() {
    double xa, ya, xb, yb;
    cin >> xa >> ya >> xb >> yb;

    printf("%.3lf", sqrt((xa - xb) * (xa - xb) + (ya - yb) * (ya - yb)));

    return 0;
}

1034:计算三角形面积

提示 1:先从坐标找三条边

标出三个点,选一条边,按1033的方法求长度。

另外两条边也各用自己的两个端点。

提示 2:边长算好再用面积公式

三条边已存入a、b、c:

double p = (a + b + c) / 2;

再用海伦公式求面积,别把坐标直接当边长;用三角形顶点交换次序检查结果。

常见错误

海伦公式里的 p 是半周长。坐标公式最后要取绝对值,再除以 2,否则交换两个顶点,可能算出负面积。

自测

输入:0 0 3 0 0 4 → 预期输出:6.00

交换两个顶点后,面积应保持不变。

方法一:海伦公式
// 求出三条边的边长
// 利用海伦公式,求三角形面积
#include <iostream>
#include <cstdio>
#include <cmath>

using namespace std;

int main() {
    double xa, ya, xb, yb, xc, yc;
    scanf("%lf%lf%lf%lf%lf%lf", &xa, &ya, &xb, &yb, &xc, &yc);

    double a = sqrt((xa - xb)*(xa - xb) + (ya - yb)*(ya - yb));
    double b = sqrt((xb - xc)*(xb - xc) + (yb - yc)*(yb - yc));
    double c = sqrt((xa - xc)*(xa - xc) + (ya - yc)*(ya - yc));

    double p = (a + b + c) / 2;
    double s = sqrt(p * (p - a) * (p - b) * (p - c));

    printf("%.2lf", s);

    return 0;
}
方法二:坐标公式(拓展)
#include <iostream>
#include <cstdio>
#include <cmath>

using namespace std;

int main() {
    double xa, ya, xb, yb, xc, yc;
    cin >> xa >> ya >> xb >> yb >> xc >> yc;
    double twice = (xb-xa)*(yc-ya) - (yb-ya)*(xc-xa);

    printf("%.2f", abs(twice) / 2);

    return 0;
}

1035:等差数列末项计算

提示 1:数清一共增加几次

数列3、7、11中,公差是多少,从第1项到第3项加了几次?

提示 2:起点加上变化总量

读入a1、a2、n后:

int d = a2 - a1;

第n项用a1 + (n - 1) * d,为什么不是加n次?

常见错误

从首项到第 n 项,只增加了 n−1 次公差。公差可以是负数,也可以是零,别默认数列一直变大。

自测

输入:3 7 5 → 预期输出:19

输入:10 7 4 → 预期输出:1

参考代码
#include <iostream>

using namespace std;

int main() {
    int a1, a2, n;
    cin >> a1 >> a2 >> n;

    int d = a2 - a1;
    int an = a1 + (n - 1) * d;

    cout << an;

    return 0;
}

1036:A×B 问题

提示 1:先估乘积能有多大

50000乘50000超过普通32位int的上限,读入值能存下不代表乘积能存下。

提示 2:乘法之前就用大类型
cout << (long long)a * b;

a、b已读入;写成(long long)(a * b)为什么可能太晚?

常见错误

long long ans = a * b; 可能先用 int 算乘积,溢出后才赋值,已经晚了。(long long)(a * b) 同样如此,要在乘法之前提升类型。

自测

输入:50000 50000 → 预期输出:2500000000

方法一:乘法前转换类型
#include <iostream>

using namespace std;

int main() {
    int a, b;
    cin >> a >> b;

    cout << (long long)a * b;

    return 0;
}
方法二:使用 1LL 提升类型
// 1LL 是 long long 类型的 1
// 先乘 1LL,让后续乘法按 long long 计算,a、b 本身的类型不变
#include <iostream>

using namespace std;

int main() {
    int a, b;
    cin >> a >> b;

    cout << 1LL * a * b;

    return 0;
}

1037:计算 2 的幂

提示 1:先算一个小的

\(2^3=2\times2\times2=8\),^在C++里不能这样用。

这一章先用<cmath>里的pow来算,循环的写法,后面学了再回来看。

提示 2:把计算结果转成整数输出

读入整数n后:

long long ret = (long long)pow(2, n);

pow(2, n)计算\(2^n\),返回浮点数;本题把结果转成整数保存。

先用n = 3核对,再试n = 0。

常见错误

^ 不是乘方。连续乘 2 时,初值是 1,不能是零。pow 返回浮点数;以后求更大的整数幂,还要留意精度和整数类型的范围,不能直接照搬。

自测

输入:0 → 预期输出:1

输入:10 → 预期输出:1024

方法一:pow函数
#include <iostream>
#include <cmath>

using namespace std;

int main() {
    int n;
    cin >> n;

    long long ret = (long long)pow(2, n);

    cout << ret;

    return 0;
}
方法二:连续乘2(拓展)
#include <iostream>

using namespace std;

int main() {
    int n;
    cin >> n;

    long long res = 1;
    while (n--) {
        res *= 2;
    }

    cout << res;

    return 0;
}
方法三:整数位移(拓展)
#include <iostream>

using namespace std;

int main() {
    int n;
    cin >> n;

    cout << (1LL << n);

    return 0;
}

1038:苹果和虫子

提示 1:正在吃的苹果也不完整了

每个苹果要4小时,过了9小时,有几个苹果不能算完整?

提示 2:先求已动过的数量

总数n、每个所需时间x、经过时间y已读入:

int eaten = y / x;
if (y % x != 0) eaten++;

剩下是n - eaten,如果算出负数该怎样处理?

常见错误

时间为零,一个苹果也没被咬过。恰好整除时不用再多扣一个;即使时间很长,完整苹果数最少也只能是零。输入顺序是 n、x、y。

自测

输入:10 4 9 → 预期输出:7

再试时间为零、时间恰好为吃一个苹果所需时间的整数倍,以及时间足够吃完全部苹果的情况。

方法一:判断余数
#include <cstdio>
#include <iostream>

using namespace std;

int main() {
    int n,y,x;
    scanf("%d%d%d",&n,&x,&y);

    int ret;
    if (y % x == 0) ret = n - y / x;
    else ret = n - y / x - 1;

    if (ret < 0) ret = 0;

    cout << ret;

    return 0;
}
方法二:向上取整公式
#include <iostream>
#include <cstdio>

using namespace std;

int main() {
    int n, x, y;
    scanf("%d%d%d", &n, &x, &y);

    int ret = n - (y + x - 1) / x;

    if (ret < 0) ret = 0;

    cout << ret;

    return 0;
}
方法三:使用 max 限制下界
#include <iostream>
#include <algorithm>

using namespace std;

int main() {
    int n, x, y;
    cin >> n >> x >> y;

    int res = max(0, n - (y + x - 1) / x);

    cout << res;

    return 0;
}
方法四:用商和余数取整
#include <iostream>
#include <algorithm>

using namespace std;

int main() {
    int n, x, y;
    cin >> n >> x >> y;
    int bad = y / x + (y % x != 0);

    cout << max(0, n - bad);

    return 0;
}