分数规划
1.算法分析
01分数规划是这样的一类问题,有一堆物品,每一个物品有一个收益 a i ai ai,一个代价 b i bi bi,我们要求一个方案,选出k个物品,使选择的 ∑ a i / ∑ b i \sum{ai}/\sum{bi} ∑ai/∑bi最大。
基本做法是采用二分法,假设当前二分到的答案为x,那么:
∑ a i / ∑ b i > = x \sum{ai}/\sum{bi}>=x ∑ai/∑bi>=x可以转化为: ∑ ( a i − x ∗ b i ) > = 0 \sum{(ai-x*b_i)}>=0 ∑(ai−x∗bi)>=0,所以每次check的时候将 a i − x ∗ b i a_i-x*b_i ai−x∗bi进行排序,取前k个,判断他们的和是否大于等于0即可
2.模板
/*poj2976:给出两个数组a和b,$a_i$代表这门课获得的成绩,$b_i$代表这门课的满分是多少,现在要求你舍弃掉k门课,使得平均绩点($100*\sum{ai}/\sum{bi}$)最高。直接二分即可,注意是舍弃k门课,所以相当于选择n-k门课,另外输出需要四舍五入,可以直接利用.0lf进行四舍五入*/
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <iostream>
#include <limits>
#include <map>
#include <set>
#include <vector>
using namespace std;
const int N = 1e6 + 5;
typedef long long LL;
int k, n;
double a[N], b[N], c[N];
const double eps = 1e-8;
// 和0做比较
int sgn(double x) {
if (fabs(x) < eps) return 0; // =0
if (x < 0)
return -1; // < 0
else
return 1; // > 0
}
bool check(double mid) {
for (int i = 0; i < n; i++) c[i] = a[i] - mid * b[i];
sort(c, c + n);
double sum = 0;
for (int i = n - 1; i >= k; i--) sum += c[i]; //选n-k项
return (sgn(sum) >= 0);
}
int main() {
while (scanf("%d%d",&n,&k)) {
if ((n + k) == 0) break;
for (int i = 0; i < n; i++) scanf("%lf", &a[i]);
for (int i = 0; i < n; i++) scanf("%lf", &b[i]);
double l = 0, r = 1; //成绩最大是1,也就是满分
while (r - l > 1e-8) {
// r与l的间隔小于1e-8
double mid = (l + r) / 2; // 不需要考虑加一的事情
if (check(mid))
l = mid;
else
r = mid; // r和l都是mid}
}
printf("%.0lf\n",l*100);//四舍五入
}
return 0;
}
3.典型例题
3.1 最优比率生成环
acwing361观光奶牛
题意: 给定一张L个点、P条边的有向图,每个点都有一个权值f[i],每条边都有一个权值t[i]。求图中的一个环,使“环上各点的权值之和”除以“环上各边的权值之和”最大。输出这个最大值。 点数N~1e3, 边数M~5e3
题解: 二分枚举答案,然后根据这个mid来重新构图,与 a i − m i d ∗ b i a_i-mid*b_i ai−mid∗bi类似,本题为$ f[t]-midw[i] 的 和 是 否 大 于 0 , 那 么 将 每 个 点 的 权 值 下 放 到 边 上 , 然 后 判 断 是 否 存 在 正 环 即 可 , 也 可 以 变 个 符 号 , 判 断 是 否 存