题目描述
海盗头子普朗克在珍宝岛上找到一批宝物,宝物有 nnn 件,第 iii 件宝物的价值为 cic_ici,重量为 wiw_iwi。普朗克想取其中的 kkk 件宝物,使得这些宝物的价值之和除以重量之和(实数除法)达到最大。请帮他求出这个最大值。
输入格式
第一行两个正整数 n,kn, kn,k。
接下来 nnn 行,每行两个正整数 ci,wic_i,w_ici,wi。
输出格式
一个数,即你所求出的最大值,保留四位小数。
输入样例
3 2
7 2
11 6
5 3
输出样例
2.4000
样例解释
选择第一个和第三个宝物,价值之和除以重量之和 (7+5)÷(2+3)=2.4(7+5) \div (2+3)=2.4(7+5)÷(2+3)=2.4,达到最大。
输入样例2
6 3
18 96
22 41
34 14
7 87
27 47
79 12
输出样例2
2.0149
数据范围
对于 100%100\%100% 的数据,1≤n,ci,wi≤1051 \leq n, c_i, w_i \leq 10^51≤n,ci,wi≤105,保证答案不超过 10910^9109。
题目解答
这竟然是一道二分答案题,作为蒟蒻的我第一次做时竟然没有看出来。首先我们应该想如何写每次二分时的 check
函数:
- 假设我们当前二分枚举到的值为 ans\text{ans}ans,选的 kkk 个物品价值之和为 ∑ci\sum c_i∑ci,重量之和为 ∑wi\sum w_i∑wi,那么求如下式子的真假即可:
∑ci∑wi≥ans \dfrac{\sum c_i}{\sum w_i} \geq \text{ans} ∑wi∑ci≥ans - 不妨对它进行变换:
∑ci∑wi≥ans=∑ci≥ans×∑wi=∑ci−∑(ans×wi)≥0=∑(ci−ans×wi)≥0 \begin{aligned} & \dfrac{\sum c_i}{\sum w_i} \geq \text{ans} \\ = & \sum c_i \geq \text{ans} \times \sum w_i \\ = & \sum c_i - \sum (\text{ans} \times w_i) \geq 0 \\ = & \sum (c_i - \text{ans} \times w_i) \geq 0 \end{aligned} ===∑wi∑ci≥ans∑ci≥ans×∑wi∑ci−∑(ans×wi)≥0∑(ci−ans×wi)≥0 - 只要我们每次将 ci−ans×wic_i - \text{ans} \times w_ici−ans×wi 的值降序排序,取前 kkk 个元素,看它们的和是不是大于 000 即可,时间复杂度 O(nlognlog(∑wi))O(n \log n \log ( \sum w_i) )O(nlognlog(∑wi))。代码如下:
AC代码
#include <bits/stdc++.h>
using namespace std;
int n, k, c[100005], w[100005];
double l, r, arr[100005];
bool check(double x)
{
double res = 0;
for (int i = 1; i <= n; i++)
arr[i] = c[i] - w[i] * x; // 计算ci - wi * x
sort(arr + 1, arr + n + 1, greater<double>()); // 降序排序
for (int i = 1; i <= k; i++) // 累加前k个数
res += arr[i];
return res >= 0;
}
int main()
{
scanf("%d %d", &n, &k);
for (int i = 1; i <= n; i++)
{
scanf("%d %d", c + i, w + i);
r += c[i];
}
while (r - l > 1e-6) // 二分答案
{
double mid = (l + r) / 2.0;
if (check(mid))
l = mid;
else
r = mid;
}
printf("%.4lf", l);
return 0;
}
好了,本期博客就到这里,若注解有误,还请各位大佬多多指教。
另外,觉得写得好的话,还可以点赞+收藏哦 ^⌣^\hat{} \smile \hat{}^⌣^