1 条题解

  • 0
    @ 2025-12-12 16:22:40

    一、 思路提示

    1. 浮点数的使用
      • 能量值在计算过程中会变成小数(比如乘以 0.15),所以必须使用 double 类型来存储能量 EE
      • 输入 EE 时也要用 double
    2. 循环结构
      • 食物链有 NN 层,意味着会发生 N1N-1 次传递。
      • 我们可以写一个循环 for(int i = 1; i < N; i++)
    3. 生存检查
      • 在计算每一级之前或之后,都需要检查当前能量。
      • 公式逻辑:先减去消耗 CC,此时要判断是否 0\le 0。如果还活着,再乘以 P/100P/100
    4. 四舍五入
      • 题目要求输出整数。C++ 中 (int)(value + 0.5) 是处理正数四舍五入的常用技巧。或者使用 round() 函数。

    二、 预备知识点总结

    1. 数据类型double 的定义与输入输出。
    2. 算术运算:理解百分比计算(×P/100.0\times P / 100.0)。
    3. 类型转换:浮点数转整数(四舍五入)。
    4. 循环与中断break 的使用(一旦灭绝立即停止)。

    三、 启发式教学:草稿纸上的推理过程

    教练:“我们把能量想象成水流,流过一层层的梯田。”

    1. 初始
      • “最大的水池(生产者)里有 10000 升水。”
    2. 第一层流动
      • “流向下一层前,先蒸发了 1000 升(消耗)。剩 9000。”
      • “这时候水流还够吗?够(>0)。”
      • “管道比较窄,只有 15% 能流下去。9000×0.15=13509000 \times 0.15 = 1350。”
      • “第二层水池收到 1350。”
    3. 第二层流动
      • “蒸发 500。剩 1350500=8501350-500=850。”
      • “流下去 20%。850×0.2=170850 \times 0.2 = 170。”
      • “第三层收到 170。”
    4. 极端情况演示
      • “假设第三层消耗 200。170200=30170 - 200 = -30。”
      • “水干了!后面的老鹰(第四层)肯定没水喝了,直接饿死。输出 -1。”

    四、 读题关键词总结

    1. “浮点数” / “四舍五入” \rightarrow 使用 double,最后用 round+0.5 强转。
    2. “小于等于 0” \rightarrow 灭绝判断条件。
    3. NN 个营养级” \rightarrow 注意循环次数是 N1N-1 次(因为是传递过程,4个点只有3条边)。

    五、 标准题解代码 (C++14)

    /**
     * 题目:生态系统的能量金字塔 (Energy Pyramid)
     * 算法:模拟 / 浮点数运算 (Simulation / Float Math)
     * 难度:GESP 4级 / CSP-J T1
     */
    
    #include <iostream>
    #include <cmath>   // 用于 round 函数
    #include <iomanip> // 虽然不需要输出小数,但习惯性包含
    
    using namespace std;
    
    int main() {
        // 1. 变量定义
        int N;
        double current_energy; // 使用 double 存储能量
    
        // 2. 输入初始数据
        if (!(cin >> N >> current_energy)) return 0;
    
        // 标志位:记录是否中途灭绝
        bool extinct = false;
    
        // 3. 循环模拟 N-1 次传递过程
        // i 表示第 i 级向第 i+1 级传递
        for (int i = 1; i < N; ++i) {
            int cost, rate;
            cin >> cost >> rate;
    
            // 步骤 A: 自身消耗
            current_energy -= cost;
    
            // 检查:是否消耗完后直接归零或负数
            if (current_energy <= 0) {
                extinct = true;
                break; // 能量耗尽,链条断裂
            }
    
            // 步骤 B: 传递效率 (百分比)
            // 注意 rate 是整数,要除以 100.0 变成小数
            current_energy = current_energy * (rate / 100.0);
    
            // 检查:传递后是否变得极小(虽然理论上不会变为负,但可能趋近0)
            // 题目要求 <= 0 即灭绝
            if (current_energy <= 0) {
                extinct = true;
                break;
            }
        }
    
        // 4. 输出结果
        if (extinct) {
            cout << -1 << endl;
        } else {
            // 四舍五入转为整数
            // 方法1: (int)(current_energy + 0.5)
            // 方法2: round(current_energy)
            cout << (long long)round(current_energy) << endl;
        }
    
        return 0;
    }
    

    时间与空间复杂度分析

    • 时间复杂度
      • 循环 N1N-1 次,每次做简单的加减乘除。
      • O(N)O(N)
    • 空间复杂度
      • 只用了几个变量。
      • O(1)O(1)

    六、 数据生成器 (Generator.cpp)

    /**
     * 题目:生态系统的能量金字塔
     * 数据生成器
     */
    
    #include <iostream>
    #include <fstream>
    #include <string>
    #include <vector>
    #include <random>
    #include <ctime>
    #include <cmath>
    #include <iomanip>
    
    
    using namespace std;
    
    mt19937 rng(time(0));
    
    // 核心解法
    long long solve(int N, double E, const vector<pair<int, int>>& params) {
        double cur = E;
        for(auto p : params) {
            cur -= p.first; // cost
            if(cur <= 0) return -1;
            cur = cur * (p.second / 100.0); // rate
            if(cur <= 0) return -1;
        }
        return (long long)round(cur);
    }
    
    void make_case(int id) {
        int N;
        double E;
        vector<pair<int, int>> params;
    
        switch(id) {
            case 1: // 样例
                N = 4; E = 10000.0;
                params = {{1000, 15}, {500, 20}, {100, 10}};
                break;
            case 2: // 第一级就死掉
                N = 5; E = 100.0;
                params = {{200, 50}, {10, 10}, {10, 10}, {10, 10}};
                break;
            case 3: // 效率极高 (100%)
                N = 5; E = 1000.0;
                for(int i=0; i<N-1; ++i) params.push_back({10, 100});
                break;
            case 4: // 效率极低 (1%)
                N = 5; E = 1000000.0;
                for(int i=0; i<N-1; ++i) params.push_back({0, 1});
                break;
            case 5: // 刚好剩下一点点
                N = 3; E = 100.0;
                // 100 - 50 = 50; * 0.1 = 5
                // 5 - 4 = 1; * 0.5 = 0.5 -> round -> 1
                params = {{50, 10}, {4, 50}}; 
                break;
            case 6: // 随机小数据
                N = 10; E = rng()%10000 + 1000;
                for(int i=0; i<N-1; ++i) {
                    int c = rng()%100;
                    int r = rng()%20 + 5; // 5%~24%
                    params.push_back({c, r});
                }
                break;
            case 7: // 随机中数据
                N = 100; E = 1000000.0;
                for(int i=0; i<N-1; ++i) {
                    int c = rng()%50;
                    int r = rng()%90 + 10;
                    params.push_back({c, r});
                }
                break;
            case 8: // 消耗很大,容易死
                N = 20; E = 100000.0;
                for(int i=0; i<N-1; ++i) {
                    int c = rng()%5000;
                    int r = 20;
                    params.push_back({c, r});
                }
                break;
            case 9: // 大数据 N=1000
                N = 1000; E = 1000000.0;
                // 为了不死,消耗必须极小,效率必须高
                for(int i=0; i<N-1; ++i) {
                    params.push_back({0, 99});
                }
                break;
            case 10: // 浮点精度测试 (刚好 round 进位)
                N = 2; E = 10.0;
                // 10 - 0 = 10; * 0.15 = 1.5 -> 2
                params = {{0, 15}}; 
                break;
        }
    
        string in_file = to_string(id) + ".in";
        ofstream fout_in(in_file);
        fout_in << N << " " << fixed << setprecision(1) << E << "\n";
        for(auto p : params) {
            fout_in << p.first << " " << p.second << "\n";
        }
        fout_in.close();
    
        string out_file = to_string(id) + ".out";
        ofstream fout_out(out_file);
        fout_out << solve(N, E, params) << "\n";
        fout_out.close();
    
        cout << "Generated Case " << id << endl;
    }
    
    int main() {
        for(int i=1; i<=10; ++i) make_case(i);
        return 0;
    }
    

    这道题将浮点数运算生态系统的能量流动结合,既考察了编程基础,又强化了“能量逐级递减”的生物学概念。对于 GESP 4 级学生来说,这是练习 double 类型和循环控制的绝佳题目。

    • 1

    信息

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