已经接触过01分数规划但是只知道二分写法(实际求解略慢),Dinkelbach算法还是值得一学的。
上一道裸的01分数规划吧。POJ
x数组代表我们选或者不选 0,1构成
R=sigma(a[i]*x[i])/sigma(b[i]*x[i])
变形 设F(v) 为 sigma(a[i]*x[i])/sigma(b[i]*x[i])不小于v
那么sigma(a[i]*x[i])/sigma(b[i]*x[i])>=v
变形 sigma(a[i]*x[i]) >= sigma(b[i]*x[i]*v)
sigma(a[i]*x[i] - b[i]*x[i]*v) >= 0
那么我们定义一个数组 c[i] = a[i] - b[i]*v;
从大到小排序c[i] 再对前k个求和 如果 sigma(c[i]) >= 0说明当前的 F(v) == true;
然后我们二分答案 nlogn 复杂度
以为eps太大wa了一发很明显的话要到小数点三位那么我们的eps就不能是 1e-3

#include <stdio.h> #include <iostream> #include <algorithm> #include <string.h> #include <math.h> using namespace std; const int maxn = 1e3+10; double a[maxn],b[maxn]; double c[maxn]; const double eps = 1e-4;//卡精度 int n,k; bool cmp(const double x,const double y) { return x>y; } bool F(double v) { for(int i=0;i<n;i++) { c[i] = a[i] - b[i]*v; } sort(c,c+n); double sum = 0.0; for(int i=0;i<n-k;i++) { sum += c[i]; } return sum>=0; } bool read() { scanf("%d%d",&n,&k); if(!(n||k)) return false; for(int i=0;i<n;i++) { scanf("%lf",&a[i]); } for(int i=0;i<n;i++) { scanf("%lf",&b[i]); } return true; } int main() { while(read()) { double lb = 0; double ub = 1; while(ub-lb>eps) { double mid = (lb+ub)/2; if(F(mid)) { lb = mid; } else { ub = mid; } } printf("%.0f ",lb*100); } return 0; }