蓝桥杯备赛:Day4-P10387 训练士兵

📚 算法笔记:P10387 训练士兵 (贪心与代价权衡)

1. 题目简述

P10387 [蓝桥杯 2024 省 A\] 训练士兵 - 洛谷](https://www.luogu.com.cn/problem/P10387) 有 n n n 个士兵需要进行训练,每个士兵有一个单独训练的费用 p i p_i pi 和需要达标的训练次数 c i c_i ci。 同时存在一种"团购"方案:花费 S S S 元可以让所有**当前还没达标**的士兵统一训练一次。 **目标**:求出让所有士兵都达标的最小总花费。 #### 2. 核心代码 (C++ AC版本) ```c++ #include using namespace std; typedef long long ll; struct Soldier { ll price, cnt; }; // 贪心核心:按需求次数从【小】到【大】排序,模拟时间线的流逝 bool cmp(Soldier a, Soldier b) { return a.cnt < b.cnt; } void solve() { ll people, Sum; if (!(cin >> people >> Sum)) return; vector queue(people); ll total_p = 0; // 记录当前所有未毕业士兵的单练总价 for (int i = 0; i < people; i++) { cin >> queue[i].price >> queue[i].cnt; total_p += queue[i].price; } // 按时间线(需求次数)排序 sort(queue.begin(), queue.end(), cmp); ll ans = 0; ll last_train = 0; // 记录已经流逝的时间(已经统一训练了多少次) for (int i = 0; i < people; i++) { // 当前这个士兵还需要练几次才能毕业? ll rounds = queue[i].cnt - last_train; if (rounds > 0) { // 在这 rounds 次训练中,每次都选择当前最便宜的方案(团购 or 全员单练) ans += rounds * min(total_p, Sum); // 时间线推进 last_train = queue[i].cnt; } // 核心交接:当前士兵毕业,退出训练群。以后的单练总价不再包含他 total_p -= queue[i].price; } cout << ans << "\n"; } int main() { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); int _ = 1; while (_--) { solve(); } return 0; } ``` #### 3. 核心考点与注意事项 ##### 🔍 核心考点 1. **代价权衡 (Trade-off) 的贪心思维** :在每一个时间切片里,我们面临两个选择:要么花 S S S 元团购,要么花 `total_p` 元单练。贪心策略就是:**每一轮都取 min ⁡ ( t o t a l _ p , S ) \\min(total\\_p, S) min(total_p,S)。** 2. **时间线模拟(横向切片法)** :不要盯着"一个人"看他要怎么练,而是要盯着"时间线"看。随着时间推移,不断有人达到 c i c_i ci 次要求并"毕业"。 3. **动态维护成本** :当一个士兵毕业时,他不再参与后续的单练,因此要在 `total_p` 中减去他的 p i p_i pi。一旦 `total_p` 跌破团购价 S S S,后续的所有训练将自动切换为单练模式。 ##### ⚠️ 注意事项 * **数据类型** :费用 × \\times × 次数极易爆 `int`,必须全线使用 `long long`。 * **时空复杂度** :使用 `sort` 排序时间复杂度为 O ( N log ⁡ N ) O(N \\log N) O(NlogN),遍历一次 O ( N ) O(N) O(N),总复杂度 O ( N log ⁡ N ) O(N \\log N) O(NlogN),完美契合 N = 10 5 N=10\^5 N=105 的数据范围。 #### 4. 易错点回顾 (My Mistakes) 1. **致命的逻辑错误:排序方向反了 (WA)** * **错误思路** :起初按需求次数**从大到小**排序,直接给需求最大的人买满团购。 * **产生后果** :忽略了在漫长的训练过程中,需求少的人会**提前毕业**。他们毕业后,剩下的单练总价可能早就比团购便宜了。 * **纠正方案** :必须**从小到大**排序。模拟时间流逝,"剥洋葱"式地一层一层结算,有人毕业就实时扣除他的单练费用。 2. **`std::vector` 的越界访问 (RE 隐患)** * **错误写法** :定义了 `vector queue(people)`,循环却写成 `for(int i = 1; i <= people; i++)`。 * **产生后果** :`vector` 的默认索引是从 0 0 0 到 s i z e − 1 size-1 size−1。从 1 1 1 开始赋值会导致第 0 0 0 个元素为空(值为 0 0 0),且访问最后一个元素时发生数组越界,最终被 `sort` 打乱导致答案全错。 * **纠正方案** :使用 `vector` 时,必须形成肌肉记忆,**永远从 0 0 0 开始遍历 (`i = 0; i < n; i++)`**。

相关推荐
智者知已应修善业2 小时前
【51单片机中的打飞机设计】2023-8-25
c++·经验分享·笔记·算法·51单片机
智者知已应修善业4 小时前
【51单片机按键调节占空比3位数码管显示】2023-8-24
c++·经验分享·笔记·算法·51单片机
徐某人..6 小时前
基于i.MX6ULL平台的智能网关系统开发
arm开发·c++·单片机·qt·物联网·学习·arm
无敌秋7 小时前
# C++ 简单工厂模式实战指南
c++·简单工厂模式
cany10007 小时前
C++ -- 模板的声明和定义
开发语言·c++
澈2077 小时前
深耕进阶 Day1:C 与 C++ 核心差异 + C++ 入门基石
c语言·开发语言·c++
脱氧核糖核酸__7 小时前
LeetCode热题100——234.回文链表(两种解法)
c++·算法·leetcode·链表
愚者游世7 小时前
noexcept 说明符与 noexcept运算符各版本异同
开发语言·c++·程序人生·面试·visual studio
极客BIM工作室8 小时前
OCCT开发实践:空间封闭曲线生成曲面的思考与总结
c++