OneCoder Avatar
OneCodercoderli.com · 937 篇博文
五级

C++ 算法考级专栏

真题分析、矩阵探测、递归回溯与基础语法

🎨 视觉封面

【GESP】C++五级练习题 luogu-B3628 机器猫斗恶龙

📅 2025-12-16·✍️ OneCoder·计算中...·⏱️ 8 分钟
#GESP#C++#贪心#前缀和

GESP C++ 五级练习题,贪心思想和前缀和思想考点。题目难度⭐⭐★☆☆,适合五级入门和四级练习,洛谷难度等级普及-

luogu-B3628 机器猫斗恶龙

题目要求

题目描述

机器猫出门斗恶龙了!他需要通过 nn 个关卡。

每个关卡要么是与怪物战斗,扣除一定的血量;要么是营地,给机器猫增加一定的血量。

在旅途中,机器猫任意时刻的血量不能低于或等于 00。问机器猫至少需要多少的初始血量,才能完成任务。

血量为正整数。

输入格式

第一行,一个正整数 nn,表示关卡数量。

第二行,共 nn 个整数 aia_i,表示每个关卡。

  • ai>0a_i>0,则表示这个关卡是营地,增加 aia_i 的血量
  • ai<0a_i<0,则表示这个关卡是战斗,机器猫血量代价为 aia_i

输出格式

仅一行,一个正整数,表示机器猫需要的初始血量。

输入输出样例 #1

输入 #1
PLAINTEXT
3
-100 -200 -300
输出 #1
PLAINTEXT
601

输入输出样例 #2

输入 #2
PLAINTEXT
5
-200 -300 1000 -100 -100
输出 #2
PLAINTEXT
501

说明/提示

样例解释

第二组样例:机器猫带着 501501 点血量出门,两场战斗之后剩下 11,恢复到 10011001,两场战斗之后为 801801,完成任务。

数据规模与约定

对于 100%100\% 的数据,n100000n \leq 100000, 1ai10001 \leq \vert a_i \vert \leq 1000


题目分析

这道题目的核心是寻找机器猫在旅途中血量可能达到的最小值,从而确定所需的最小初始血量。我们可以通过前缀和的方法来解决这个问题。

1. 问题建模

  • 机器猫通过 nn 个关卡,每个关卡有血量变化 aia_i(正数表示增加,负数表示减少)。
  • 要求机器猫在旅途中的任意时刻血量不能低于或等于 00(即必须始终为正整数)。
  • 求机器猫完成任务所需的最小初始血量。

2. 解题思路:前缀和

  1. 血量变化的前缀和

    • pre[i] 表示机器猫从第 1 关到第 ii 关结束时,血量的总变化量
    • pre[0] = 0 (初始状态,未开始关卡,变化量为 0)。
    • pre[i] = pre[i-1] + a[i]
    • 如果机器猫的初始血量为 HinitialH_{\text{initial}},那么在通过第 ii 关后的血量就是 Hcurrent=Hinitial+pre[i]H_{\text{current}} = H_{\text{initial}} + \text{pre}[i]
  2. 寻找最低血量点

    • 为了保证机器猫在任意时刻血量都大于 00,我们需要找到在所有 pre[i] 中,值最小的那一个(记为 min_pre)。这个 min_pre 代表了机器猫在旅途中相对初始血量,血量下降最多的情况。
    • min_pre = min(pre[1], pre[2], ..., pre[n])
  3. 计算最小初始血量

    • 我们要求在所有关卡通过后(包括每关通过的瞬间),血量都必须大于 00
    • 即对于所有的 i[1,n]i \in [1, n],都需要满足 Hinitial+pre[i]1H_{initial} + pre[i] \geq 1(因为血量必须为正整数)。
    • 所以,最小初始血量 Hinitial_min=1min_preH_{initial\_min} = 1 - \text{min}\_pre
  4. 特殊情况考虑

    • 如果 min_pre > 0,说明机器猫的血量全程都是增加的(或者说从未低于初始血量)。在这种情况下,只需要初始血量为 11 即可保证全程血量大于 00

3. 算法步骤

  1. 读取输入:读取关卡数量 nn 和每个关卡的血量变化 aia_i
  2. 计算前缀和:遍历 aia_i,计算 pre[i] = pre[i-1] + a[i]
  3. 寻找最小前缀和:遍历 pre 数组,找到其最小值 min_pre
  4. 计算并输出结果
    • 如果 min_pre>0\text{min}\_pre \gt 0,输出 11
    • 如果 min_pre0\text{min}\_pre \le 0,输出 -min_pre+1\text{-min}\_pre + 1

示例代码

CPP
#include <climits>
#include <iostream>

// 数组大小定义,稍大于 100000 即可
int a[100005];
// 前缀和数组,用于记录到达每一关时的累计血量变化
int pre[100005];

int main() {
    int n;
    std::cin >> n;  // 输入关卡数量
    // 输入每一关的血量变化,并计算前缀和
    // a[i] > 0 表示营地回血,a[i] < 0 表示战斗扣血
    for (int i = 1; i <= n; i++) {
        std::cin >> a[i];
        pre[i] = pre[i - 1] +
                 a[i];  // 计算前缀和:pre[i] 表示经过前 i 关后血量的总变化量
    }

    int min_a = INT_MAX;
    // 遍历所有关卡,找到前缀和的最小值
    // 这个最小值代表了旅途中血量相对于初始血量的最低点
    for (int i = 1; i <= n; i++) {
        min_a = std::min(min_a, pre[i]);
    }

    // 计算需要的初始血量
    // 假设初始血量为 H,则任意时刻的血量为 H + pre[i]。
    // 题目要求 H + pre[i] > 0,即 H > -pre[i]。
    // 为了保证所有时刻都满足,需要 H > -min(pre[i]),即 H >= -min_a + 1。
    if (min_a <= 0) {
        // 如果过程中血量相对于初始值有减少(min_a <=
        // 0),则需要足够的初始血量来抵消最大亏空 例如最低降到了
        // -100,则初始至少需要 101,才能保证最低点为 1
        std::cout << -min_a + 1 << std::endl;
    } else {
        // 如果 min_a >
        // 0,说明全程血量相对于初始值都是增加的(或从未低于初始值且始终增加)
        // 只要初始血量为 1(题目要求正整数),就能保证最低点血量 > 0
        std::cout << 1 << std::endl;
    }
    return 0;
}

💡 OneCoder 资源指引

所有代码开源上传至 GitHub:yummy-code 仓库 · GESP 专题站:GESP WIKI

🤝 技术交流与答疑

欢迎加入:C++ GESP/CSP 考级答疑群(688906745)Java/Python交流群(982860385),点击可直接加群。

📚

猜你想读 · 相关文章推荐

OneCoder

OneCoder (lihongzheshuai)

一个中年人的自留地,记录学习 C++、GESP/NOI、Java、Python 与算法架构的心得体会。本站唯一网址:coderli.com

💬 读者留言与交流

0 条讨论
✨ 支持 Markdown 语法格式
还没有留言,快来成为第一个讨论者吧!