poj 2976 Dropping tests 01分数规划

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;
}
posted @ 2020-11-21 07:25  C锥  阅读(86)  评论(0编辑  收藏  举报