题解:P5719 【深基4.例3】分类平均

· · 题解

P5719【深基4.例3】分类平均

分析

要找到所有 A、B 两类数各自的均值,可以从小到大枚举 1\sim n 范围内的每个整数 i,同时维护四个变量 \mathrm{sumA}\mathrm{sumB}\mathrm{cntA}\mathrm{cntB},分别表示当前找到的 A、B 两类数的数字之和与个数。那么最终两类数的均值就分别是 \frac{\mathrm{sumA}}{\mathrm{cntA}}\frac{\mathrm{sumB}}{\mathrm{cntB}}

想要判断一个数字 i 是 A 类数还是 B 类数,只需要查看 i 除以 k 的余数。如果余数为 0,则是 A 类数,否则是 B 类数。

代码

C++

在 C++ 里,可以用如下的 for 循环将 i1 取到 n 进行枚举:for (int i = 1; i <= n; ++i) { Block }。而 Block 的内容需要根据前文的分析,用 if-else 结构判断 i 属于哪一类数,然后进行相应的累加,可以写出如下的代码:

for (int i = 1; i <= n; ++i) {
  if (i % k == 0) { // i 是 A 类数
    sumA += i;    // 累加 A 类数的和
    ++cntA;       // 累加 A 类数的个数
  } else {        // 否则是 B 类数
    sumB += i;    // 累加 B 类数的和
    ++cntB;       // 累加 B 类数的数量
  }
}

由于 for 循环的循环体里只有一个 if-else 结构,包裹循环体的花括号可以省略,只留 if-else 结构自身的花括号,结合相应的变量声明和读入,可以写出如下代码:

#include <bits/stdc++.h>
using namespace std;

int main() {
  int n, k;
  cin >> n >> k;
  int sumA = 0, sumB = 0, cntA = 0, cntB = 0;
  for (int i = 1; i <= n; ++i) if (i % k == 0) { // 这里的花括号是 if 的
    sumA += i;
    ++cntA;
  } else {
    sumB += i;
    ++cntB;
  }
  cout << fixed << setprecision(1) << 1.0 * sumA / cntA << " " << 1.0 * sumB / cntB << endl; // 按规定进行输出
}

这里输出较为复杂。其中 fixed 是要求总是按小数形式进行输出(而不是科学计数法),setprecision(x) 表示后面的浮点数保留的小数点后位数为 x

因为 sumAcntA 都是整数,直接对两个整数做除法得到的结果是整除而不是浮点数形式,所以必须在运算前先用浮点数 1.0 乘一下。这是因为:浮点数乘以整数的结果是浮点数。在从左到右运算时,先算 1.0 * sumA,得到了一个浮点类型的结果,把这个结果去除 cntA,就是浮点型除以整形,结果就能得到浮点型了。

Rust

在 Rust 里,进行与上文类似的循环的方法是 for i in 1..=n { Block }。相比循环 for _ in 0..n { Block },这里有两个改变:

  1. 把循环变量的声明从 _ 改为了 i。这是因为 Rust 不允许在代码里拿到 _ 的值(可以改成 _ 试一下),但是我们需要在循环体里拿到循环变量的值进行判断,所以需要用一个不是 _ 的标识符。
  2. 循环范围从 0..n 变成了 1..=n。前者的循环范围是 0,1,2,\dots n - 1n 个数,后者是 1,2,\dots , nn 个数。其中对右边界的取等是通过 = 标识的。

Rust 不允许省略 for 循环体的大括号,而 Rust 的 if 条件无需加括号,可以写出代码如下:

/*
  省略了读入类 
  https://www.luogu.com.cn/paste/clle55rg
*/

fn main() {
  let mut cin = Scanner::new();
  let (n, k) = (cin.next_int(), cin.next_int());
  let (mut sum_a, mut sum_b, mut cnt_a, mut cnt_b) = (0, 0, 0, 0);
  for i in 1..=n {
    if i % k == 0 {
      sum_a += i;
      cnt_a += 1;
    } else {
      sum_b += i;
      cnt_b += 1;
    }
  }
  println!("{:.1} {:.1}", sum_a as f64 / cnt_a as f64, sum_b as f64 / cnt_b as f64);
}

关于输出,{:.1} 是输出一位小数的方法,Rust 不允许浮点数和整数相除,因此必须把两个量都显式地转换成 f64 浮点型。

TypeScript

在 ts 里,类似的 for 循环方法是 for (let i = 1; i <= n; ++i) { Block }。它的 for 和 if 风格都和 cpp 几乎一致。值得一提的是 number 本身就是浮点型,因此无需做类型转换就可以直接除。

import { Reader } from './Reader';
// https://www.luogu.com.cn/paste/r7vtnbaz
import * as fs from 'node:fs';

const data = fs.readFileSync('/dev/stdin')
const lines = data.toString('ascii').trim().split('\n');
const cin = new Reader(lines);

let n = cin.nextInt(), k = cin.nextInt();
let sumA = 0, sumB = 0, cntA = 0, cntB = 0;
for (let i = 1; i <= n; ++i) if (i % k == 0) {
  sumA += i;
  cntA++;
} else {
  sumB += i;
  cntB++;
}

console.log(`${(sumA / cntA).toFixed(1)} ${(sumB / cntB).toFixed(1)}`)