poj 2976 Dropping tests 01分数规划
题目连接
01分数规划板子题.
这里具体说一下01分数规划式子的推导.
我们要让这个东西最大化: \(\displaystyle \frac{\sum_{i - 1}^{n} a_i * x_i}{\sum_{i = 1}^{n} b_i * x_i}\), 其中\(x_i\)取值为0或1.
简单来说就是给你一堆二元组\((a_i, b_i)\), 去掉其中几个, 让他们的比值最大.
我们考虑这样一个式子:\(\displaystyle \sum_{i = 1}^{n} (a_i - L * b_i) * x_i\), 是否大于等于0. 把这个式子拆开, 就会变成:\(\displaystyle \frac{\sum_{i - 1}^{n} a_i * x_i}{\sum_{i = 1}^{n} b_i * x_i} >= L\).
也就是是否存在一组解\(\left \{ x_1, x_2, ..., x_n\right \}\), 使得比值大于等于\(L\), 如果存在, 说明这个\(L\)比我们想要求的答案小, 如果不存在, 则这个\(L\)比我们想要的比值大.
这与二分答案的判定条件很像, 所以我们就可以二分这个\(L\), 判断\(\displaystyle \sum_{i = 1}^{n} (a_i - L * b_i) * x_i\)是否大于0就可以了.(具体的判定方法就是取最大的那几个\((a_i - L * b_i)\)就好了).
对于本题, 它让你恰好删去\(k\)个二元组使答案最大, 那么就要选\(n - k\)个最大的\((a_i - L * b_i)\).最后别忘了给答案乘上100.(这sb题精度卡了我半天)
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
inline long long read() {
long long s = 0, f = 1; char ch;
while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
return s * f;
}
const int N = 1111;
const double eps = 1e-15;
int n, k;
double a[N], b[N], tmp[N];
double ans;
int cmp(double a, double b) { return a < b; }
int calc(double L) {
double res = 0;
for(register int i = 1;i <= n; i++)
tmp[i] = a[i] - L * b[i];
sort(tmp + 1, tmp + n + 1, cmp);
for(register int i = n;i >= k + 1; i--) res += tmp[i];
return res >= 0;
}
int main() {
while(1) {
n = read(); k = read(); if(n == 0 && k == 0) break;
for(register int i = 1;i <= n; i++) scanf("%lf", &a[i]);
for(register int i = 1;i <= n; i++) scanf("%lf", &b[i]);
double l = 0, r = 1e9, mid;
while(r - l > eps) {
mid = (l + r) / 2;
if(calc(mid)) l = mid;
else r = mid;
}
printf("%.0lf\n", mid * 100);
}
return 0;
}