2 条题解

  • 0
    @ 2025-12-1 17:16:15

    对于这道题目,编程的核心在于将化学逻辑转化为数学逻辑。我们不需要编写复杂的 if-else 来判断谁是“短板”,而是可以利用一种更优雅的思维方式:计算每种原料理论上能生产多少产品,然后取最小值。

    这正如“木桶效应”:桶的容量取决于最短的那块木板。

    C++14 标准解答代码

    /**
     * 题目: 星际引擎的化学计量 (Stoichiometry of the Stars)
     * 语言: C++14 (NOIP Standard)
     * 算法: 模拟 / 数学 (Math)
     * 核心思想: 限制性反应物 (Limiting Reagent) 即取最小值
     */
    
    #include <iostream>
    #include <iomanip>    // 用于 setprecision 控制输出精度
    #include <algorithm>  // 用于 std::min 取最小值
    
    using namespace std;
    
    int main() {
        // 1. IO 提速
        // 对于浮点数输入输出,虽然不像海量整数那样耗时,但在竞赛中保持这个习惯是好的
        ios_base::sync_with_stdio(false);
        cin.tie(NULL);
    
        double m_h2, m_o2;
      // 1. 输入氢气质量和氧气质量
        cin >> m_h2 >> m_o2;
    
        // 2. 宏观转微观:计算摩尔数 (Moles)
        // 氢气 H2 摩尔质量 = 2 g/mol
        double n_h2 = m_h2 / 2.0;
        // 氧气 O2 摩尔质量 = 32 g/mol
        double n_o2 = m_o2 / 32.0;
    
        // 3. 寻找限制性因素 (The Limiting Reagent)
        // 反应方程式: 2 H2 + 1 O2 -> 2 H2O
        
        // 假设1: 氢气完全反应
        // 关系: 2 mol H2 生成 2 mol H2O (比例 1:1)
        double potential_water_from_h2 = n_h2;
    
        // 假设2: 氧气完全反应
        // 关系: 1 mol O2 生成 2 mol H2O (比例 1:2)
        double potential_water_from_o2 = n_o2 * 2.0;
    
        // 实际生成量取决于“短板”,即两者中的较小值
        double n_water = min(potential_water_from_h2, potential_water_from_o2);
    
        // 4. 微观转宏观:计算水的质量
        // 水 H2O 摩尔质量 = 18 g/mol
        double m_water = n_water * 18.0;
    
        // 5. 格式化输出
        // fixed + setprecision(2) 保证输出两位小数
        cout << fixed << setprecision(2) << m_water << endl;
    
        return 0;
    }
    

    阿西莫夫的代码注释

    1. 逻辑简化
      • 初学者常用的写法是:if (n_h2 / 2.0 < n_o2) ... else ...
      • 高阶写法(如本代码):分别计算出“如果氢气耗尽能造多少水”和“如果氧气耗尽能造多少水”,然后直接 std::min()。这种写法不仅代码更短,而且更能体现**“受限于瓶颈”**的物理本质。
    2. 常量的魔法:代码中出现了 2.0, 32.0, 18.0。在正式工程中,我们通常会定义为 const double MOLAR_MASS_H2 = 2.0;,但在算法竞赛(NOIP)的短代码中,直接写出这些众所周知的化学常数是可接受的,只要注释清晰即可。
    3. 精度控制:始终使用 double 而不是 float,因为 float 的精度在多次乘除后可能会产生肉眼可见的误差,而 double 在 OI 竞赛中是处理小数的标配。
    • 0
      @ 2025-12-1 17:08:44

      阿西莫夫的解题指南

      这道题是化学计算在编程中的直接映射,核心在于 “寻找限制性反应物”(Limiting Reagent)。

      步骤逻辑:

      1. 宏观转微观(计算摩尔数)

        • 氢气物质的量:nH2=m1/2.0n_{H2} = m_1 / 2.0
        • 氧气物质的量:nO2=m2/32.0n_{O2} = m_2 / 32.0
      2. 寻找短板(化学计量比判断): 方程式告诉我们需求比例是 H2:O2=2:1H_2 : O_2 = 2 : 1

        • 如果我们要消耗掉所有的氧气,需要 2×nO22 \times n_{O2} 的氢气。
        • 判断
          • 如果 nH22×nO2n_{H2} \ge 2 \times n_{O2}:说明氢气够用(或者过量),氧气是限制因素。此时反应受限于氧气。
            • 根据方程式 1mol O22mol H2O1 mol \ O_2 \rightarrow 2 mol \ H_2O
            • 生成水的摩尔数 nwater=2×nO2n_{water} = 2 \times n_{O2}
          • 反之(nH2<2×nO2n_{H2} < 2 \times n_{O2}):说明氢气不够,氢气是限制因素
            • 根据方程式 2mol H22mol H2O2 mol \ H_2 \rightarrow 2 mol \ H_2O(即 1:1)。
            • 生成水的摩尔数 nwater=nH2n_{water} = n_{H2}
      3. 微观转宏观(计算质量)

        • mwater=nwater×18.0m_{water} = n_{water} \times 18.0

      C++ 标准解答与数据生成器

      以下代码包含Solution(标准解答)和Generator(数据生成器)。请保存为 gen_mole.cpp 并运行。

      /**
       * Problem: Stoichiometry of the Stars (Mole Calculation)
       * Author: Isaac Asimov (AI)
       * Concept: Limiting Reagent in Chemistry
       */
      
      #include <iostream>
      #include <iomanip>
      #include <algorithm>
      #include <fstream>
      #include <random>
      #include <string>
      
      using namespace std;
      
      // ==========================================
      // Part 1: 标准解答逻辑 (The Solver)
      // ==========================================
      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 m_h2, m_o2;
              cin >> m_h2 >> m_o2;
      
              // 1. Calculate moles (n = m / M)
              double n_h2 = m_h2 / 2.0;
              double n_o2 = m_o2 / 32.0;
      
              double n_water = 0;
      
              // 2. Determine Limiting Reagent based on 2:1 ratio
              // Needed H2 for all O2 is (n_o2 * 2)
              if (n_h2 >= n_o2 * 2.0) {
                  // Hydrogen is sufficient/excess. Oxygen limits the reaction.
                  // Ratio O2 : H2O = 1 : 2
                  n_water = n_o2 * 2.0;
              } else {
                  // Hydrogen is insufficient. Hydrogen limits the reaction.
                  // Ratio H2 : H2O = 2 : 2 (i.e., 1:1)
                  n_water = n_h2;
              }
      
              // 3. Convert back to mass (m = n * M)
              double m_water = n_water * 18.0;
      
              cout << fixed << setprecision(2) << m_water << endl;
              
              cin.close();
              cout.close();
              cout << "Generated: " << out_file << endl;
          }
      };
      
      // ==========================================
      // Part 2: 数据生成逻辑 (The Generator)
      // ==========================================
      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);
          fout << fixed << setprecision(1);
      
          double h2, o2;
      
          if (case_id == 1) {
              // Sample 1: Perfect match (4g H2, 32g O2) -> 2:1 mol
              h2 = 4.0; o2 = 32.0;
          }
          else if (case_id == 2) {
              // Sample 2: O2 excess
              h2 = 4.0; o2 = 100.0;
          }
          else if (case_id == 3) {
              // Sample 3: H2 excess
              h2 = 10.0; o2 = 32.0;
          }
          else if (case_id == 4) {
              // Edge case: Zero fuel
              h2 = 0.0; o2 = 50.0;
          }
          else if (case_id <= 7) {
              // Random scenarios
              h2 = rand_double(0.1, 100.0);
              o2 = rand_double(0.1, 100.0);
          }
          else {
              // Large numbers
              h2 = rand_double(1000.0, 5000.0);
              o2 = rand_double(1000.0, 5000.0);
          }
      
          fout << h2 << " " << o2 << endl;
          fout.close();
      }
      
      int main() {
          cout << "--- Generating Stoichiometry 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. 逻辑的清晰度:代码并没有直接操作质量,而是先转成摩尔,再进行逻辑判断,最后转回质量。这是解决化学计算题最不容易出错的“标准范式”。
      2. 教育意义:这道题强迫学生去理解方程式 2H2+O22H_2 + O_2 中那个系数 2 的含义。很多初学者会误以为质量比也是 2:1(即 2g 氢气配 1g 氧气),通过编写这个程序,他们会深刻意识到质量比其实是 4:324:32 (即 1:81:8)。

      希望这道题能让学生们明白,在宇宙中航行,数学比勇气更可靠。

      • 1

      信息

      ID
      19239
      时间
      1000ms
      内存
      32MiB
      难度
      (无)
      标签
      (无)
      递交数
      0
      已通过
      0
      上传者