[蓝桥杯]黛玉泡茶【算法赛】

问题描述

话说林黛玉闲来无事,打算在潇湘馆摆个茶局,邀上宝钗、探春她们一起品茗赏花。黛玉素来讲究,用的茶杯也各有不同,大的小的,高的矮的,煞是好看。这不,她从柜子里拿出了 NN 只茶杯,这 NN 只茶杯的容量分别是 C1,C2,…,CNC1​,C2​,…,CN​ 。为了泡茶,黛玉还特意准备了一个容量为 MM 的茶壶。

天气炎热,姐妹们都想着多喝点,所以至少得斟满 KK 杯茶才够大家喝。可是这茶壶来回取水太麻烦了,黛玉想尽量少跑几趟。

对此,请你帮黛玉算算,她最少需要用茶壶取多少次水,才能斟满至少 KK 杯茶呢?

输入描述

第一行包含三个整数 NN、MM 和 KK(1≤K≤N≤1031≤K≤N≤103,1≤M≤1031≤M≤103),分别表示茶杯的数量、茶壶的容量以及黛玉想要斟满的茶杯数量。

第二行包含 NN 个整数 C1,C2,…,CNC1​,C2​,…,CN​(1≤Ci≤1031≤Ci​≤103),表示每个茶杯的容量。

输出描述

输出一个整数,表示黛玉最少需要用茶壶取水的次数。

样例输入

2 3 1
5 7

样例输出

2

运行限制

语言最大运行时间最大运行内存
C++1s256M
C1s256M
Java2s256M
Python33s256M
PyPy33s256M
Go3s256M
JavaScript3s256M

总通过次数: 1472  |  总提交次数: 1903  |  通过率: 77.4%

难度: 简单   标签: 排序, 数学

算法思路

本问题需要求解黛玉最少需要取水多少次才能斟满至少 K 个茶杯。核心思路是:​​选择容量最小的 K 个茶杯,计算它们的总容量,然后根据茶壶容量 M 计算最小取水次数​​。具体步骤如下:

  1. ​排序选择​​:将 N 个茶杯按容量升序排序,选择前 K 个容量最小的茶杯。因为容量小的茶杯更容易组合填充,减少取水次数。
  2. ​总容量计算​​:计算这 K 个茶杯的总容量 sum
  3. ​取水次数计算​​:最小取水次数为 ceil(sum / M)(向上取整)。公式为:
    ans=⌈Msum​⌉=Msum+M−1​
贪心策略正确性证明
  • ​总容量约束​​:斟满 K 个茶杯至少需要 sum 单位水,每次取水最多提供 M 单位,因此取水次数至少为 ⌈Msum​⌉。
  • ​可行性​​:每次取水后可任意分配水量给多个茶杯(可多次分配给同一茶杯),因此总水量 sum 可被 M 整除分配。
  • ​最优性​​:选择最小 K 个茶杯使 sum 最小化,从而最小化取水次数。

算法过程演示

以样例 N=2, M=3, K=1, 茶杯=[5, 7] 为例:

  1. ​排序选择​​:排序后茶杯为 [5, 7],选择最小 K=1 个茶杯 → 容量 5
  2. ​总容量计算​​:sum = 5
  3. ​计算取水次数​​:
    ans=⌈35​⌉=35+3−1​=37​=2(整数除法)。

倒水分配过程:

  • ​第一次取水​​:3 单位 → 倒入茶杯 3 单位(剩余需 2 单位)。
  • ​第二次取水​​:3 单位 → 倒入茶杯 2 单位(斟满)。

C++完整代码

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main() {
    int N, M, K;
    cin >> N >> M >> K;
    vector<int> cups(N);
    for (int i = 0; i < N; i++) {
        cin >> cups[i];
    }

    // 升序排序
    sort(cups.begin(), cups.end());

    // 计算前K个最小茶杯的总容量
    int sum = 0;
    for (int i = 0; i < K; i++) {
        sum += cups[i];
    }

    // 计算最小取水次数: (sum + M - 1) / M 实现向上取整
    int ans = (sum + M - 1) / M;
    cout << ans << endl;

    return 0;
}

代码解析

  1. ​输入处理​​:读入茶杯数量 N、茶壶容量 M、需斟满茶杯数 K 及茶杯容量列表。
  2. ​排序​​:使用 sort 对茶杯容量升序排序,确保选择最小 K 个茶杯。
  3. ​求和​​:计算前 K 个茶杯的总容量 sum
  4. ​取水次数计算​​:
    • 公式 (sum + M - 1) / M 等价于 ⌈Msum​⌉。
    • 整数除法特性:当 sum 不能被 M 整除时,+ M - 1 确保向上取整。
  5. ​输出结果​​:直接输出最小取水次数。

测试用例与验证

测试用例 (N, M, K, 茶杯容量)输出说明
2 3 1 [5, 7]2样例:最小茶杯容量5 > M,需2次取水
3 5 3 [2, 3, 4]2总容量9,ceil(9/5)=2
3 10 2 [1, 100, 100]1最小K个[1,100],总容量101,ceil(101/10)=11?
​注意​​:应选最小2个[1,100],sum=101 → ans=11
4 5 2 [1, 1, 5, 10]2最小K个[1,1],sum=2,ceil(2/5)=1?
​修正​​:K=2选[1,1],sum=2 → ans=1
3 1 3 [1, 1, 1]3M=1,每次只能斟1单位,需3次
1000 1000 1000 [全1]1总容量1000,ceil(1000/1000)=1

​注​​:测试用例 3 10 2 [1,100,100] 中,最小2个茶杯为 [1,100]sum=101ans=11;测试用例 4 5 2 [1,1,5,10] 中,最小2个茶杯为 [1,1]sum=2ans=1

注意事项

  1. ​茶杯选择​​:必须选择容量最小的 K 个茶杯,以最小化总容量 sum
  2. ​整数除法​​:公式 (sum + M - 1) / M 依赖整数除法特性,确保向上取整。
  3. ​边界情况​​:
    • 当 M ≥ sum 时,取水次数为 1。
    • 当 M = 1 时,取水次数等于 sum(每次只能斟1单位)。
  4. ​性能​​:排序复杂度 O(NlogN),N ≤ 1000,满足时限要求。

优化建议

  1. ​部分排序​​:若 K 远小于 N,使用 nth_element 选择前 K 个元素(复杂度 O(N)):
    
    
    	
    nth_element(cups.begin(), cups.begin() + K, cups.end());
    sort(cups.begin(), cups.begin() + K); // 确保前K个有序

  2. ​提前终止​​:当 K 较小时,求和循环可提前终止。
  3. ​大数处理​​:若容量和可能溢出(如 sum > INT_MAX),改用 long long 存储 sum

数学证明

设所选茶杯容量为 C1​≤C2​≤⋯≤CK​:

  • ​最小总容量​​:S=∑i=1K​Ci​ 是最小可能值。
  • ​取水次数下限​​:由于 S≥CK​,有 ⌈S/M⌉≥⌈CK​/M⌉,故 ans=⌈S/M⌉ 已满足约束。

此解法高效且正确,适用于所有合法输入。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

鑫鑫向栄

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值