【算法刷题】蓝桥杯:怎么倒茶(贪心算法 + 结构体排序)

【算法刷题】蓝桥杯:怎么倒茶(贪心算法 + 结构体排序)

📌 题目链接与描述

  • 题目名称:怎么倒茶
  • 题目链接蓝桥云课 - 怎么倒茶
  • 数据规模 : 1 ≤ n ≤ 10 4 1 \le n \le 10^4 1≤n≤104, 1 ≤ a i ≤ b i ≤ 10 6 1 \le a_i \le b_i \le 10^6 1≤ai≤bi≤106, ∑ a i ≤ W ≤ 10 9 \sum a_i \le W \le 10^9 ∑ai≤W≤109, 1 ≤ s i ≤ 10 6 1 \le s_i \le 10^6 1≤si≤106
  • 核心问题 :有 W W W 毫升茶水分配给 n n n 位领导。第 i i i 位领导的倒茶量 x i x_i xi 须满足 a i ≤ x i ≤ b i a_i \le x_i \le b_i ai≤xi≤bi,产生的开心度为 s i × x i s_i \times x_i si×xi。求所有领导开心度之和的最大值。

💡 解题核心思路:贪心算法(Greedy)

为了使总开心度最大,我们可以将倒茶过程分为两个阶段:

1. 基础保底分配(满足硬性下限)

  • 每个人必须至少 得到 a i a_i ai 毫升茶。
  • 先给所有人各倒 a i a_i ai 毫升茶,扣除后茶壶剩余茶量为: left_w = W − ∑ a i \text{left\_w} = W - \sum a_i left_w=W−∑ai。
  • 此时获得的基础开心度为: min_s = ∑ ( a i × s i ) \text{min\_s} = \sum (a_i \times s_i) min_s=∑(ai×si)。

2. 贪心追加分配(追求最大收益)

  • 给完下限后,第 i i i 位领导最多还能接受 ( b i − a i ) (b_i - a_i) (bi−ai) 毫升茶。
  • 每多给 1 毫升茶带来的额外收益是 s i s_i si,因此 s i s_i si 越大,性价比越高
  • 策略 :按开心度权值 s i s_i si 从大到小排序 。优先把剩余茶量 left_w \text{left\_w} left_w 分配给 s i s_i si 最高的领导,直到填满其上限 b i b_i bi 或茶水用完。

❌ 常见踩坑与易错点

  1. vector 未初始化长度(导致段错误/运行错误)
    • vector<Person> p; cin >> p[i].a;(访问越界崩溃)。
    • vector<Person> p(n); 明确指定大小。
  2. 变量名与 C++ 标准库冲突
    • 在使用 using namespace std; 时,不要声明变量名为 left(会与 std::left 冲突导致编译错误),建议使用 left_wrest_w
  3. 数据类型溢出
    • 茶水量 W ≤ 10 9 W \le 10^9 W≤109,开心度权值 s i ≤ 10 6 s_i \le 10^6 si≤106,累加的总开心度可达 10 15 10^{15} 1015 级别,必须全部使用 long long

💻 最终 AC 代码 (C++)

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

struct Person {
  long long a, b, s;
};

// 按照开心度权值 s 从大到小排序
bool cmp(Person a, Person b) {
  return a.s > b.s;
}

int main() {
  // 快速 I/O
  ios::sync_with_stdio(false);
  cin.tie(nullptr);

  long long n, w;
  long long min_s = 0;
  if (!(cin >> n >> w)) return 0;

  vector<Person> p(n); // 正确分配 n 个元素的空间
  long long min_a = 0;

  for (int i = 0; i < n; i++) {
    cin >> p[i].a >> p[i].b >> p[i].s;
    min_a += p[i].a;               // 累加必须分配的下限
    min_s += p[i].a * p[i].s;       // 累加基础开心度
  }

  long long left_w = w - min_a;     // 剩余可自由分配的茶量
  
  // 按权值降序排序
  sort(p.begin(), p.end(), cmp);

  // 贪心追加
  for (int i = 0; i < n && left_w > 0; i++) {
    long long temp = min(p[i].b - p[i].a, left_w); // 实际能给的量
    left_w -= temp;
    min_s += temp * p[i].s;
  }

  cout << min_s << "\n";

  return 0;
}

⏱️ 复杂度分析

  • 时间复杂度 : O ( n log ⁡ n ) \mathcal{O}(n \log n) O(nlogn)
    • 读入数据 :需要 O ( n ) \mathcal{O}(n) O(n)。
    • 排序瓶颈 :对 n n n 个领导按开心度权值 s i s_i si 排序,耗时 O ( n log ⁡ n ) \mathcal{O}(n \log n) O(nlogn)。
    • 遍历追加 :分配剩余茶水仅需 O ( n ) \mathcal{O}(n) O(n)。
    • 总体评估 :当 n = 10 4 n = 10^4 n=104 时,总计算量仅为 10 5 10^5 105 次左右,实际运行时间在 5ms 内,轻松 AC。
  • 空间复杂度 : O ( n ) \mathcal{O}(n) O(n),主要用于存储 n n n 个结构体对象的 vector
相关推荐
伟大的车尔尼2 小时前
贪心题目:玩筹码
贪心
伟大的车尔尼1 天前
贪心题目:数组大小减半
贪心
漂流瓶jz1 天前
UVA-1609 不公平竞赛 题解答案代码 算法竞赛入门经典第二版
数据结构·算法·链表·贪心·aoapc·算法竞赛入门经典·uva
伟大的车尔尼4 天前
贪心的概念
数据结构·算法·贪心
吞下星星的少年·-·5 天前
The 2025 ICPC Asia East Continent Online Contest (I) A题(模拟+贪心)
算法·贪心·模拟
aqiu11111114 天前
【算法刷题】蓝桥杯/AtCoder:删除元素后的中位数问题(Symmetry / Median)
算法·蓝桥杯·排序·中位数
aqiu11111116 天前
【算法刷题】蓝桥杯:0星际争霸(大数比较 + 多关键字自定义排序)
算法·排序·自定义排序
OuO-218 天前
笔试强训 Day 44:最小差值、kotori 和素因子、dd 爱科学 1.0
二分查找·动态规划·贪心·模拟·记忆化搜索·递归·状态表示优化
OuO-220 天前
笔试强训 Day 43:kotori 和抽卡(二)、ruby 和薯条、循环汉诺塔
数学·前缀和·二分查找·动态规划·排序·滑动窗口·循环汉诺塔