问题描述
话说林黛玉闲来无事,打算在潇湘馆摆个茶局,邀上宝钗、探春她们一起品茗赏花。黛玉素来讲究,用的茶杯也各有不同,大的小的,高的矮的,煞是好看。这不,她从柜子里拿出了 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++ | 1s | 256M |
C | 1s | 256M |
Java | 2s | 256M |
Python3 | 3s | 256M |
PyPy3 | 3s | 256M |
Go | 3s | 256M |
JavaScript | 3s | 256M |
总通过次数: 1472 | 总提交次数: 1903 | 通过率: 77.4%
难度: 简单 标签: 排序, 数学
算法思路
本问题需要求解黛玉最少需要取水多少次才能斟满至少 K 个茶杯。核心思路是:选择容量最小的 K 个茶杯,计算它们的总容量,然后根据茶壶容量 M 计算最小取水次数。具体步骤如下:
- 排序选择:将 N 个茶杯按容量升序排序,选择前 K 个容量最小的茶杯。因为容量小的茶杯更容易组合填充,减少取水次数。
- 总容量计算:计算这 K 个茶杯的总容量
sum
。 - 取水次数计算:最小取水次数为
ceil(sum / M)
(向上取整)。公式为:
ans=⌈Msum⌉=Msum+M−1
贪心策略正确性证明
- 总容量约束:斟满 K 个茶杯至少需要
sum
单位水,每次取水最多提供 M 单位,因此取水次数至少为 ⌈Msum⌉。 - 可行性:每次取水后可任意分配水量给多个茶杯(可多次分配给同一茶杯),因此总水量
sum
可被 M 整除分配。 - 最优性:选择最小 K 个茶杯使
sum
最小化,从而最小化取水次数。
算法过程演示
以样例 N=2, M=3, K=1, 茶杯=[5, 7]
为例:
- 排序选择:排序后茶杯为
[5, 7]
,选择最小 K=1 个茶杯 → 容量5
。 - 总容量计算:
sum = 5
。 - 计算取水次数:
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;
}
代码解析
- 输入处理:读入茶杯数量
N
、茶壶容量M
、需斟满茶杯数K
及茶杯容量列表。 - 排序:使用
sort
对茶杯容量升序排序,确保选择最小 K 个茶杯。 - 求和:计算前 K 个茶杯的总容量
sum
。 - 取水次数计算:
- 公式
(sum + M - 1) / M
等价于 ⌈Msum⌉。 - 整数除法特性:当
sum
不能被M
整除时,+ M - 1
确保向上取整。
- 公式
- 输出结果:直接输出最小取水次数。
测试用例与验证
测试用例 (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] | 3 | M=1,每次只能斟1单位,需3次 |
1000 1000 1000 [全1] | 1 | 总容量1000,ceil(1000/1000)=1 |
注:测试用例
3 10 2 [1,100,100]
中,最小2个茶杯为[1,100]
,sum=101
,ans=11
;测试用例4 5 2 [1,1,5,10]
中,最小2个茶杯为[1,1]
,sum=2
,ans=1
。
注意事项
- 茶杯选择:必须选择容量最小的 K 个茶杯,以最小化总容量
sum
。 - 整数除法:公式
(sum + M - 1) / M
依赖整数除法特性,确保向上取整。 - 边界情况:
- 当
M ≥ sum
时,取水次数为 1。 - 当
M = 1
时,取水次数等于sum
(每次只能斟1单位)。
- 当
- 性能:排序复杂度 O(NlogN),N ≤ 1000,满足时限要求。
优化建议
- 部分排序:若 K 远小于 N,使用
nth_element
选择前 K 个元素(复杂度 O(N)):nth_element(cups.begin(), cups.begin() + K, cups.end()); sort(cups.begin(), cups.begin() + K); // 确保前K个有序
- 提前终止:当 K 较小时,求和循环可提前终止。
- 大数处理:若容量和可能溢出(如
sum > INT_MAX
),改用long long
存储sum
。
数学证明
设所选茶杯容量为 C1≤C2≤⋯≤CK:
- 最小总容量:S=∑i=1KCi 是最小可能值。
- 取水次数下限:
由于 S≥CK,有 ⌈S/M⌉≥⌈CK/M⌉,故 ans=⌈S/M⌉ 已满足约束。
此解法高效且正确,适用于所有合法输入。