语法周赛round29
T1¶
首先,计算蛋挞液的总体积。
-
鸡蛋 每个鸡蛋的体积为 $V_{\text{egg}}$,共使用 $e$ 个。 鸡蛋的总体积为 $$ e \times V_{\text{egg}} $$
-
牛奶 每瓶牛奶的体积为 $V_{\text{milk}}$,共使用 $m$ 瓶。 牛奶的总体积为 $$ m \times V_{\text{milk}} $$
因此,蛋挞液的总体积为 $$ V = e \times V_{\text{egg}} + m \times V_{\text{milk}} $$
已知每个蛋挞皮可以容纳 $V_{\text{tart}}$ 的蛋挞液,则最多可以制作的蛋挞数量为
其中,$\lceil x \rceil$ 表示 大于等于 $x$ 的最小整数。例如:
- $\lceil 1.1 \rceil = 2$
- $\lceil 1 \rceil = 1$
- $\lceil -1.1 \rceil = -1$
在 C++ 中,整数除法是向 0 取整的。
在 cmath 库中提供了 ceil(x) 函数,用于计算 $\lceil x \rceil$ 的值。(注意使用时确保 $x$ 为 double 类型结果建议保存到整数类型中)
烤箱每一批最多可以烤制 $t$ 个蛋挞,因此所需的烤制批次数为 $$ \left\lceil \frac{n}{t} \right\rceil $$
T2¶
题目给定一个长方形的长 $a$ 和面积 $S$,要求求出这个长方形的周长 $L$。
假设长方形的宽为 $b$,由长方形的面积公式 $S=a\times b$,可以得出 $b=S\div a$。在计算出 $b$ 后,计算 $L=(a+b)\times 2$ 即可。
由于题目保证 $S$ 是 $a$ 的倍数,因此计算出的 $b=S\div a$ 一定是整数,$L$ 也一定是整数。因此,我们使用 int 存储所有的变量即可。
代码编写时,首先定义变量 $a,S$,之后读入。之后,计算 $b=S\div a$ 和 $L=(a+b)\times 2$,并输出 $L$ 即可。
T3¶
题目假设一个池塘被一个闸门分为两半。左侧一半水面高度为 $a$ 米,右侧水面高度为 $b$ 米。假设闸门降到 $c$ 米高,询问水流的流向。
我们不妨首先考虑在哪些情况下水流的流向会是从左向右、从右向左、不流动。
- 如果水流从左向右流动,那么一定会满足以下条件:
- 左侧水面高于右侧水面 $a>b$;
- 左侧水面高于闸门 $a>c$,这是水能够流动的必须要求。
满足上面两条条件后,水流一定会从左向右流动。如果任何一条条件不满足,水则一定不会流动。
- 水流从右向左流动同理,一定会满足以下条件:
- 右侧水面高于左侧水面 $b>a$;
- 右侧水面高于闸门 $b>c$。
除上述两种情况外,其他任何情况下水都不会流动。
因此我们可以编写如下代码:
// 变量定义、读入部分省略
if (a > b && a > c)
{
cout << "LeftToRight";
}
else if (b > a && b > c)
{
cout << "RightToLeft";
}
else
{
cout << "None";
}
T4¶
从 $x$ 站坐到 $y$ 站,顺时针需要坐 $(y−x+n)\bmod n$ 站,逆时针需要坐 $n−(y−x+n)\bmod n$ 站。($\bmod$ 表示取模运算,即取余数)
下面是上面结论的推导过程,我们只需要考虑清楚顺时针乘坐的站数即可,因为逆时针一定需要乘坐完剩下全部的站点:
- 当 $y>x$:
- 顺时针乘坐,站点编号由小变大,需要乘坐 $y−x$ 站路
- 逆时针乘坐,需要乘坐 $n−(y−x)$ 站路
- 当 $y<x$:
- 顺时针乘坐,站点编号先从 $x$ 由小变大至 $n$,再由 $n$ 变化为 $1$,再依次增长至 $y$。$x\to n$ 共 $n−x$ 站;$n\to 1$ 共 $1$ 站;$1\to y$ 共 $y−1$ 站。合计 $y−x+n$ 站。
- 逆时针乘坐,需要乘坐 $n−(y−x+n)$ 站。
综合两个方面,顺时针乘坐 $(y−x+n)\bmod n$ 站,逆时针用 $n$ 减去前式即可。用两个方向需要乘坐的站点数进行比较,输出对应的答案即可。
C++语言中,"被用于标识字符串的开头与结尾。如果字符串中需要包含字符",需要用转义符\标识。例如,"a\"b"在C++语言中,表示字符串a"b。
T5¶
题目要求将输入的一系列用空格隔开的整数,转换为用逗号隔开的格式。
我们首先先读取整数 $n$,之后使用 for 循环读取 $n$ 个整数。
对于每个整数,我们可以按照以下规则输出:
- 第一个整数直接输出。(不加逗号)
- 从第二个整数开始,每个整数前加上一个
,再输出。cout << "," << x;
按照这样的规则,可以完美符合题目要求的情况,且不需要编写很复杂的代码。
T6¶
题目描述了一种环形游走的方式,老师从第 $1$ 号小朋友开始,按照逆时针方向,移动 $m$ 次,每次的步数由当前位置小朋友衣服上的数字决定。由于小朋友是围成一圈的,因此当位置小于 $1$ 时,需要回到最后一个小朋友。
首先按照题目要求读入 $n,m$ 和 $a$ 数组。
之后,我们可以使用一个 pos 变量,记录老师的位置。老师的起始位置是 $1$ 号小朋友,因此 pos 初始值为 $1$。
接下来模拟移动过程。我们循环做 $m$ 次如下操作:
-
读取当前位置小朋友衣服上的数字 $a_{pos}$,计算新的位置。
-
由于是逆时针移动,每次移动 $a_{pos}$ 步,新的位置计算方式如下:
$$ pos=pos−a_{pos} $$
- 如果 $pos\leq 0$,表示已经超出了第 $1$ 号小朋友,需要重新回到小朋友处。方法是,将 $pos$ 加上若干个 $n$,直到 $pos$ 重新回到 $1\sim n$ 的范围内。
【坑点】此处的一个坑点是,因为 $a_pos$ 很有可能远大于 $n$,所以只在 $pos$ 上加一个 $n$ 是不够的。我们需要不断在 $pos$ 上 $+n$,直到 $pos$ 不再 $\leq 0$ 为止。
while (pos <= 0) {
pos += n;
}
- 进行 $m$ 次移动后,最终位置即为答案。
int pos = 1;
for (int j = 1; j <= m; j++) {
pos -= a[pos];
while (pos <= 0) pos += n;
}
cout << pos;
T7¶
题目要求在一个 $n\times m$ 的棋盘上,找出所有数值为 $x$ 的格子,并统计其中有至少一个相邻格子数值为 $y$ 的格子数量。
首先二重 for 循环遍历棋盘,查找数值为 $x$ 的格子位置。
int cnt = 0; // 记录答案数量
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (a[i][j] == x) {
// ...
}
}
}
每个找到的 $(i,j)$ 格子,其上下左右四个方向的相邻格子分别为 $(i−1,j),(i+1,j),(i,j−1),(i,j+1)$。检查这四个格子,如果某个相邻格子的值为 $y$,则该格子符合要求,计入答案。
if (a[i - 1][j] == y || a[i + 1][j] == y || a[i][j - 1] == y || a[i][j + 1] == y) {
cnt++;
}
【坑点】此处我们没有检查四个相邻格子是否在 $(1\sim n,1\sim m)$ 范围内。对于本题,如果我们有定义冗余长度的数组(例如 int a[1005][1005]),那么超出范围的格子会落在冗余区域或 $0$ 区域(如 int a[0][XXX])内。对于全局数组,这两个区域内的数值默认都是 $0$,一定不会等于 $\geq 1$ 的 $y$,因此不会对答案造成影响。但是,在很多题目中,检查相邻格子是否超出范围是必须的,请务必注意这一点。
最终输出 cnt 即可。
T8¶
题目要求将 $n$ 表示为一个缪零数与一个质数的和,并统计方案数,同时找出方案中最小的差值。
定义回顾 我们首先回顾缪零数与质数的定义:
- 缪零数:$n$ 是某个 $m^2$ 的倍数($m\geq 2$),这隐含了 $n\geq 4$。
- 质数:$n$ 不能被 $2\sim n−1$ 中任意一个整数整除,且 $n\not= 1$。
我们不妨首先考虑如何判断缪零数和质数。
缪零数 对于一个整数 $i$,我们可以遍历 $k=2\sim i−1$,逐个判断 $i$ 是否是 $k\times k$ 的倍数。如果有任何 $k$ 满足 $i$ 是 $k\times k$ 的倍数,则 $i$ 是缪零数。
int flag = 0; // 记录 i 是否是缪零数,1 代表是,0 代表不是
for (int k = 2; k <= i - 1; k++) {
if (i % (k * k) == 0) { // i 除以 k * k 的余数是 0,代表了 i 是 k * k 的倍数
flag = 1;
break;
}
}
质数 同理,对于一个整数 $i$,我们遍历 $k=2\sim i−1$,逐个判断 $i$ 是否是 $k$ 的倍数。如果有任何 $k$ 满足 $i$ 是 $k$ 的倍数,则 $i$ 不是质数。反之,则 $i$ 是质数。
int flag = 1; // 记录 i 是否是质数,1 代表是,0 代表不是
for (int k = 2; k <= i - 1; k++) {
if (i % k == 0) {
flag = 0;
break;
}
}
有了上面两部分代码,我们只需要在最外层套一个 for 循环枚举把 $n$ 分拆的方案即可。例如,我们可以枚举 $i=4\sim n−2$ 并假定 $i$ 是拆出来的缪零数,那么 $j=n−i$(范围 $2\sim n−4$)则被假定是拆出来的质数。
for (int i = 4; i <= n - 2; ++i) {
int j = n - i;
// 分别判断 i, j 是否是缪零数、质数
// 此处直接使用上面提到的代码部分即可,故省略
...
}
在找到每一种合法的分拆方案后,我们可以使用两个变量 $c,d$ 分别用于统计答案要求的数量和差的最小值。前者直接计数,后者使用擂台法即可。
int c = 0;
int d = 10000; // 由于 i - j 一定不会超过 n,n 一定不会超过 10000,因此这里 d = 10000 即可
for (int i = 4; i <= n - 2; ++i) {
int j = n - i;
// 分别判断 i, j 是否是缪零数、质数
if (i, j 中有一个不满足条件) continue;
c++;
if (i < j) d = min(d, j - i);
else d = min(d, i - j);
}
cout << c << endl << d;