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 ight }), 使得比值大于等于(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
", mid * 100);
}
return 0;
}