洛谷P10505 & POJ2976 Dropping tests 题解 & 01分数规划模板
洛谷P10505 & POJ2976 Dropping tests 题解 & 01分数规划模板
题目链接:P10505 Dropping Test
题意:
在某个课程中,你需要进行 \(n\) 次测试。
如果你在共计 \(b_i\) 道题的测试 \(i\) 上的答对题目数量为 \(a_i\),你的累积平均成绩就被定义为 \[ 100\times \dfrac{\sum_{i=1}^n a_i}{\sum_{i=1}^n b_i} \] 给定您的考试成绩和一个正整数 \(k\),如果您被允许放弃任何 \(k\) 门考试成绩,您的累积平均成绩的可能最大值是多少。
假设您进行了 \(3\) 次测试,成绩分别为 \(5/5,0/1\) 和 \(2/6\)。
在不放弃任何测试成绩的情况下,您的累积平均成绩是 \[ 100\times \frac{5+0+2}{5+1+6} \approx 58.33 \approx 58 \] 然而,如果你放弃第三门成绩,则您的累积平均成绩就变成了 \[ 100\times \frac{5+0}{5+1}\approx 83.33\approx 83 \] 输入格式:
输入包含多组测试用例,每个测试用例包含三行。
对于每组测试用例,第一行包含两个整数 \(n\) 和 \(k\)。
第二行包含 \(n\) 个整数,表示所有的 \(a_i\)。
第三行包含 \(n\) 个整数,表示所有的 \(b_i\)。
当输入用例 \(n=k=0\) 时,表示输入终止,且该用例无需处理。
输出格式:
对于每个测试用例,输出一行结果,表示在放弃 \(k\) 门成绩的情况下,可能的累积平均成绩最大值。
结果应四舍五入到最接近的整数。
数据范围:
\(1 \le n \le 1000\), \(0 \le k < n\), \(0 \le a_i \le b_i \le 10^9\)。
经典的 01分数规划 问题,也有称这个为分数规划的。
注意到题目要求的就是选 \(n-k\) 个物品使得 \(\sum a_i \times \dfrac{1}{\sum b_i}\) 的值尽可能的大。
不妨假设 \[ \sum a_i \times \dfrac{1}{\sum b_i} \ge x \] 移项可得 \[ \sum a_i - x \sum b_i \ge 0 \] 显然我们要求的就是 \(x\) 的最大值。考虑二分 \(x\) 的值,记 \(c_i = a_i-xb_i\) ,
每次检查只需暴力计算一下 \(c_i\) ,然后排序一下取最大的 \(n-k\) 个 \(c_i\) 算一算就好啦。
时间复杂度 \(O(-\log_2 \epsilon\cdot n \log n)\) ,取 \(\epsilon = 10^{-8}\) 就肯定够了,答案只需要四舍五入。
代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
template<typename T> void up(T &x, T y) { x < y ? x = y : 0; }
template<typename T> void down(T &x, T y) { x > y ? x = y : 0; }
#define rep(i, a, b) for(int i = (a), i##END = (b); i <= i##END; i++)
#define Rep(i, a, b) for(int i = (a), i##END = (b); i >= i##END; i--)
#define N ((int)(2e3 + 15))
const double eps = 1e-8;
int n, k, a[N], b[N]; double c[N];
bool check(double x)
{
rep(i, 1, n) c[i] = a[i] - x * b[i];
sort(c + 1, c + 1 + n); double res = 0;
rep(i, k + 1, n) res += c[i];
return res >= 0;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
// freopen("check.in","r",stdin);
// freopen("check.out","w",stdout);
cout << fixed << setprecision(0);
while(cin >> n >> k && n > 0)
{
rep(i, 1, n) cin >> a[i];
rep(i, 1, n) cin >> b[i];
double l = 0, r = 1;
while(abs(r - l) >= eps)
{
double mid = (l + r) / 2.0;
check(mid) ? l = mid : r = mid;
}
cout << l * 100 << '\n';
}
return 0;
}



![洛谷P17815 [ICPC 2019 Nanchang R] Who is the Champion 题解](/qwq/img/p182.webp)
