1 条题解
-
0
阿西莫夫的解题指南
这道题目虽然数学原理简单,但在编程实现上有两个陷阱:
- 科学计数法的输入:C++ 的
cin和double类型天然支持科学计数法(例如输入1e-7),不需要特殊解析,直接读入即可。 - 对数函数:C++ 标准库
<cmath>提供了log10(x)函数,用于计算以 10 为底的对数。- 公式实现:
double ph = -log10(c);
- 公式实现:
- 浮点数比较:这是新手的噩梦。算出
7.0000000000001并不等于7。- 必须使用微小值 Epsilon (EPS) 来判断相等。即:如果不等式
abs(ph - 7.0) < 1e-6成立,我们就认为它是中性。
- 必须使用微小值 Epsilon (EPS) 来判断相等。即:如果不等式
C++ 标准解答 (NOIP C++14)
/** * 题目: 异星之海的 pH 试纸 * 作者: Isaac Asimov (AI) * 知识点: 数学函数(log10), 浮点数精度控制 */ #include <iostream> #include <cmath> // 包含 log10, fabs #include <iomanip> // 包含 setprecision #include <string> using namespace std; // 定义精度阈值 const double EPS = 1e-6; int main() { // IO 优化 ios_base::sync_with_stdio(false); cin.tie(NULL); double c; if (!(cin >> c)) return 0; // 1. 计算 pH 值 // 核心公式:pH = -log10(C) double ph = -log10(c); // 2. 输出 pH 值,保留1位小数 cout << fixed << setprecision(1) << ph << endl; // 3. 判断酸碱性 // 注意:判断浮点数相等必须用误差范围 if (fabs(ph - 7.0) <= EPS) { cout << "Neutral" << endl; } else if (ph < 7.0) { cout << "Acidic" << endl; } else { cout << "Alkaline" << endl; } return 0; }
数据生成器 (Generator)
这个生成器重点在于生成跨度极大的浮点数(从 到 )。我们利用
pow(10, -x)来构造这些数据。请保存为
gen_ph.cpp并运行。#include <iostream> #include <fstream> #include <random> #include <cmath> #include <string> #include <iomanip> using namespace std; // ========================================== // Part 1: 标准解答逻辑 // ========================================== class Solution { public: void solve(string in_file, string out_file) { ifstream cin(in_file); ofstream cout(out_file); if (!cin.is_open()) return; double c; cin >> c; double ph = -log10(c); cout << fixed << setprecision(1) << ph << endl; // 这里的 EPS 和题目要求保持一致 if (fabs(ph - 7.0) <= 1e-6) cout << "Neutral" << endl; else if (ph < 7.0) cout << "Acidic" << endl; else cout << "Alkaline" << endl; cin.close(); cout.close(); cout << "Generated: " << out_file << endl; } }; // ========================================== // Part 2: 数据生成逻辑 // ========================================== mt19937 rng(2025); double rand_double(double min, double max) { uniform_real_distribution<double> dist(min, max); return dist(rng); } void generate_input(int case_id) { string filename = to_string(case_id) + ".in"; ofstream fout(filename); // 我们需要生成 1e-14 到 1.0 的数 // 等价于生成 exponent 在 [-14, 0] 之间 double exponent; if (case_id == 1) { // 样例: 纯中性 1e-7 exponent = -7.0; } else if (case_id == 2) { // 样例: 酸性 0.01 exponent = -2.0; } else if (case_id == 3) { // 边界: 极强酸 (浓度 1.0 -> pH 0) exponent = 0.0; } else if (case_id == 4) { // 边界: 极强碱 (浓度 1e-14 -> pH 14) exponent = -14.0; } else if (case_id <= 7) { // 随机生成 exponent = rand_double(-14.0, 0.0); } else { // 构造接近 7.0 的边缘情况 // pH 在 6.99 或 7.01 附近 double center = -7.0; double noise = rand_double(-0.2, 0.2); exponent = center + noise; } // 构造浓度值 C = 10^exponent double c = pow(10.0, exponent); // 为了让输入文件看起来更自然,有时用小数,有时用科学计数法 if (case_id % 2 == 0 && c >= 1e-4) { fout << fixed << setprecision(10) << c << endl; // 小数形式 } else { fout << scientific << setprecision(10) << c << endl; // 科学计数法 } fout.close(); } int main() { cout << "--- Generating pH Data ---" << endl; Solution solver; for (int i = 1; i <= 10; i++) { generate_input(i); string in = to_string(i) + ".in"; string out = to_string(i) + ".out"; solver.solve(in, out); } cout << "--- Done! ---" << endl; return 0; }阿西莫夫的点评
这道题目虽然代码量很少,但它精准地击中了初中信息学与数学、化学的交汇点:
- 对数的直观理解:学生会发现,浓度 变化 10 倍,pH 值才变化 1。这就是对数“降维打击”的威力。
- 科学计数法:这是很多初学者容易忽略的输入格式,通过这道题可以让他们习惯
1e-7这种计算机原生表达。 - 精度意识:如果学生直接写
if (ph == 7.0),在大部分随机测试点(除了完美的 )都会挂掉。这是对“计算机不是数学”这一真理的再一次教育。
- 科学计数法的输入:C++ 的
- 1
信息
- ID
- 19238
- 时间
- 1000ms
- 内存
- 32MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者