POJ 2976
题目
有n件珠宝,价值为 v i v_i vi,质量为 w i w_i wi,选取k件珠宝,使单位重量珠宝的价值最大。
标签
二分,重载运算符
输入
第一行输入n k,之后n行每行输入两个整数,分别为v和w
v , w ≤ 1 e 6 v, w\leq 1e6 v,w≤1e6且 ∑ v , ∑ w ≤ 1 e 7 \sum v, \sum w\leq 1e7 ∑v,∑w≤1e7
输出
输出用空格分隔的k个整数,表示选取的k件珠宝的index。
思路
在0到1e7之间进行二分。如果平均重量可以达到mid,那么 ∑ i v i ∑ i w i ≥ m i d \frac{\sum_i v_i}{\sum_i w_i}\geq mid ∑iwi∑ivi≥mid,移项有 ∑ i ( a i − m i d ∗ b i ) ≥ 0 \sum_i (a_i-mid*b_i)\geq0 ∑i(ai−mid∗bi)≥0。选取 v i − m i d ∗ w i v_i-mid*w_i vi−mid∗wi最大的k件珠宝,判断是否可行。
注意
- 精度达到1e-5即可
- 需要记录珠宝的初始index,可以考虑创建一个struct
- 每次改变mid时,都需要重新排序,很容易TLE。解决方法如下
- 重载运算符,而非传入函数。效率提高3倍。
- 提前计算v-mid*w,而非在排序时进行计算。
代码
#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
using namespace std;
const int N = 1e5+7;
const double eps = 1e-5;
double mid;
struct Jewel{
int v, w, ind;
double val;
void calc(double x) {
val = v - x * w;
}
bool operator<(const Jewel j) {
return val > j.val;
}
};
Jewel j[N];
double l = 0, r = 1e7+7;
//bool cmp(Jewel j1, Jewel j2) {
// return j1.val > j2.val;
//}
int main() {
cin.tie(0);
ios::sync_with_stdio(false);
int n, k;
cin >> n >> k;
for (int i = 0; i < n; ++i) {
scanf("%d %d", &j[i].v, &j[i].w);
j[i].ind = i+1;
}
while (r - l > eps) {
mid = (l + r) / 2;
double s = 0;
for (int i = 0; i < n; ++i) {
j[i].calc(mid);
}
sort(j, j+n);
for (int i = 0; i < k; ++i) {
s += j[i].val;
}
if (s > 0) {
l = mid;
} else {
r = mid;
}
}
for (int i = 0; i < k; ++i) {
cout << j[i].ind << " ";
}
return 0;
}