题解:洛谷 B4359 分糖果
【题目来源】
洛谷:B4359 [GESP202506 三级] 分糖果 - 洛谷
【题目描述】
有 \(n\) 位小朋友排成一队等待老师分糖果。第 \(i\) 位小朋友想要至少 \(a_i\) 颗糖果,并且分给他的糖果数量必须比分给前一位小朋友的糖果数量更多,不然他就会不开心。
老师想知道至少需要准备多少颗糖果才能让所有小朋友都开心。你能帮帮老师吗?
【输入】
第一行,一个正整数 \(n\),表示小朋友的人数。
第二行,\(n\) 个正整数 \(a_1,a_2,…,a_n\),依次表示每位小朋友至少需要的糖果数量。
【输出】
输出一行,一个整数,表示最少需要准备的糖果数量。
【输入样例】
4
1 4 3 3
【输出样例】
16
【核心思想】
-
问题分析:给定 \(n\) 位小朋友,第 \(i\) 位至少需要 \(a_i\) 颗糖果,且每位小朋友分得的糖果必须严格多于前一位。求最少需要准备的总糖果数。这是一个贪心策略问题,核心在于在满足单调递增约束的前提下,让每个小朋友分得尽可能少的糖果。
-
算法选择:
- 贪心构造:从左到右遍历,每位小朋友的糖果数取 \(a_i\) 和 \(s_{i-1} + 1\) 的最大值,确保既满足最低需求又满足严格递增
-
关键步骤:
- 读入数据:读取 \(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\)
-
时间/空间复杂度:
- 时间复杂度:\(O(n)\),单次线性遍历
- 空间复杂度:\(O(n)\),存储 \(s\) 数组(可优化为 \(O(1)\) 滚动变量)
-
贪心策略的核心思想:
- 局部最优保证全局最优:对于第 \(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
浙公网安备 33010602011771号