跳转至

语法周赛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}}$ 的蛋挞液,则最多可以制作的蛋挞数量为

\[ n = \left\lceil \frac{V}{V_{\text{tart}}} \right\rceil \]

其中,$\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;