1 条题解

  • 0
    @ 2025-12-1 16:57:31

    阿西莫夫的解题指南

    这道题目虽然数学原理简单,但在编程实现上有两个陷阱:

    1. 科学计数法的输入:C++ 的 cindouble 类型天然支持科学计数法(例如输入 1e-7),不需要特殊解析,直接读入即可。
    2. 对数函数:C++ 标准库 <cmath> 提供了 log10(x) 函数,用于计算以 10 为底的对数。
      • 公式实现:double ph = -log10(c);
    3. 浮点数比较:这是新手的噩梦。算出 7.0000000000001 并不等于 7
      • 必须使用微小值 Epsilon (EPS) 来判断相等。即:如果不等式 abs(ph - 7.0) < 1e-6 成立,我们就认为它是中性。

    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)

    这个生成器重点在于生成跨度极大的浮点数(从 11101410^{-14})。我们利用 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;
    }
    

    阿西莫夫的点评

    这道题目虽然代码量很少,但它精准地击中了初中信息学与数学、化学的交汇点:

    1. 对数的直观理解:学生会发现,浓度 CC 变化 10 倍,pH 值才变化 1。这就是对数“降维打击”的威力。
    2. 科学计数法:这是很多初学者容易忽略的输入格式,通过这道题可以让他们习惯 1e-7 这种计算机原生表达。
    3. 精度意识:如果学生直接写 if (ph == 7.0),在大部分随机测试点(除了完美的 10710^{-7})都会挂掉。这是对“计算机不是数学”这一真理的再一次教育。
    • 1

    信息

    ID
    19238
    时间
    1000ms
    内存
    32MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者