题解:洛谷 B4359 分糖果

【题目来源】

洛谷:B4359 [GESP202506 三级] 分糖果 - 洛谷

【题目描述】

\(n\) 位小朋友排成一队等待老师分糖果。第 \(i\) 位小朋友想要至少 \(a_i\) 颗糖果,并且分给他的糖果数量必须比分给前一位小朋友的糖果数量更多,不然他就会不开心。

老师想知道至少需要准备多少颗糖果才能让所有小朋友都开心。你能帮帮老师吗?

【输入】

第一行,一个正整数 \(n\),表示小朋友的人数。

第二行,\(n\) 个正整数 \(a_1,a_2,…,a_n\),依次表示每位小朋友至少需要的糖果数量。

【输出】

输出一行,一个整数,表示最少需要准备的糖果数量。

【输入样例】

4
1 4 3 3

【输出样例】

16

【核心思想】

  1. 问题分析:给定 \(n\) 位小朋友,第 \(i\) 位至少需要 \(a_i\) 颗糖果,且每位小朋友分得的糖果必须严格多于前一位。求最少需要准备的总糖果数。这是一个贪心策略问题,核心在于在满足单调递增约束的前提下,让每个小朋友分得尽可能少的糖果。

  2. 算法选择

    • 贪心构造:从左到右遍历,每位小朋友的糖果数取 \(a_i\)\(s_{i-1} + 1\) 的最大值,确保既满足最低需求又满足严格递增
  3. 关键步骤

    • 读入数据:读取 \(n\) 和数组 \(a[1..n]\)
    • 贪心构造\(i\)\(1\)\(n\)):
      • \(s_i \leftarrow \max(a_i, s_{i-1} + 1)\)
      • 解释:\(s_i\) 至少为 \(a_i\)(满足最低需求),且至少为 \(s_{i-1} + 1\)(满足严格多于前一位)
    • 累加求和\(ans \leftarrow \sum_{i=1}^{n} s_i\)
    • 输出结果\(ans\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n)\),单次线性遍历
    • 空间复杂度:\(O(n)\),存储 \(s\) 数组(可优化为 \(O(1)\) 滚动变量)
  5. 贪心策略的核心思想

    • 局部最优保证全局最优:对于第 \(i\) 位小朋友,在满足所有约束条件下取最小值,即 \(\max(a_i, s_{i-1} + 1)\)。这个局部最优选择不会影响后续的选择(因为后续只需满足 \(> s_i\)),因此贪心策略正确
    • 单调递增的最低成本:严格递增序列的最低成本构造方式是每次只比前一位多 \(1\),即 \(s_i = s_{i-1} + 1\)。但当 \(a_i\) 更大时,必须以 \(a_i\) 为下限
    • 前缀依赖:第 \(i\) 位的最优解只依赖于前一位的值,具有无后效性,适合从左到右的贪心处理
    • 空间优化:实际上只需维护前一个 \(s_{i-1}\) 的值,无需完整数组,可优化为 \(O(1)\) 空间
    • 适用于带约束的单调序列构造、最小成本满足条件类贪心问题

【算法标签】

普及- #贪心

【代码详解】

#include <bits/stdc++.h>
using namespace std;

#define int long long  // 定义int为long long类型
const int N = 1005;    // 定义数组最大长度

int n;                 // 输入的数字个数
int a[N];              // 存储原始数字数组
int s[N];              // 存储处理后的数字数组
int ans = 0;           // 存储最终结果

signed main()
{
    // 输入数字个数
    cin >> n;

    // 输入n个数字
    for (int i = 1; i <= n; i++)
        cin >> a[i];

    // 处理数组,确保s[i]至少比前一个大1
    for (int i = 1; i <= n; i++)
        s[i] = max(a[i], s[i - 1] + 1);

    // 计算处理后数组的总和
    for (int i = 1; i <= n; i++)
    {
        ans += s[i];
    }

    // 输出最终结果
    cout << ans << endl;

    return 0;
}

【运行结果】

4
1 4 3 3
16
posted @ 2026-07-17 13:57  团爸讲算法  阅读(12)  评论(0)    收藏  举报