CF1676E - Eating Queries
题目
Eating Queries - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
思路
- 由题意很容易得知,我们每次都吃最大的糖分的糖,得到的答案一定最小。
- 可以先对数组进行排序,从大到小吃糖,但数据量很大不能一个个吃。
- 可以对排序后的数组求前缀和数组。
- 设前缀和数组pre[N],pre[i]表示吃i(0 ~ n)颗糖可以得到的最大糖分。
- 设target为所需糖分,我们可以对pre数组进行二分找到最小的糖数,结束条件l > r
- 当pre[mid] < target时,说明吃mid颗糖还不够,所以l = mid + 1;
- 当pre[mid] >= target时,说明吃mid颗足够了但颗数不一定是最小的,
- 所以还要继续二分,当二分结束时得到的颗数才是最小的,r = mid - 1。
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 7;
int T;
int main() {
cin >> T;
// T组测试样例
while (T--) {
// 原始数组
int arr[N] = {0};
// 前缀和数组
int pre[N] = {0};
// n颗糖,k个询问
int n, k;
scanf("%d%d", &n, &k);
// 读入数据
for (int i = 0; i < n; i++) {
scanf("%d", &arr[i]);
}
// 从大到小排序,定义自定义排序,lambda表达式实现
sort(arr, arr + n, [](int a, int b){return a > b;});
// 下标从1开始,应为0颗为0糖分
for (int i = 1; i <= n; i++) {
// 计算前缀和
pre[i] = arr[i - 1] + pre[i - 1];
}
// k组询问
while (k--) {
// 二分区间[1, n]颗糖
int l = 1, r = n;
// 目标糖分
int target;
scanf("%d", &target);
// 初始化ans为-1,若不存在即为-1
int ans = -1;
// 当l > r时跳出循环,当l == r可能存在答案
while (l <= r) {
// 计算中间值,利用减法防止爆数据范围
int mid = l + (r - l) / 2;
// pre[mid] < target 颗数还不够
if (pre[mid] < target) {
// 左边的太小舍去
l = mid + 1;
} else {
// pre[mid] >= target 颗数够了
// 更新ans
ans = mid;
// 右边已经考虑过了
r = mid - 1;
}
}
// 输出答案
printf("%d\n", ans);
}
}
return 0;
}
复杂度分析
- 时间复杂度O(T * n * logn)
- 空间复杂度O(N)