01分数规划,简单的来说,就是有一些二元组(si,pi),从中选取一些二元组,使得∑si / ∑pi最大(最小)。
这种题一类通用的解法就是,我们假设x = ∑si / ∑pi的最大(小)值,那么就有x * ∑pi = ∑si ,即∑si - x * ∑pi= 0。也就是说,当某一个值x满足上述式子的时候,它就是要求的值。我们可以想到枚举……不过再想想,这个可以二分答案。
所以我们直接二分答案,当上述式子>0,说明答案小了,<0则说明答案大了,这样计算即可。
这是一种解决问题的方法,具体应该怎么做我们要看题来分析。
01分数规划有这样几种基本的题型(当然还有很多别的……暂时不在juruo的考虑范围内)
1.01分数规划
2.最优比率生成树
3.最优比率生成环
(当然还有什么最优比率最小割等等……不在juruo当前研究范围之内)
1.01分数规划。
基础题:poj2976
这个就是一开始说的这么一码事……选取n-k(原题是不选k)个物品使得比率最大。
我们考虑对上面的式子进行转化,之后我们只要给每个物品重新赋值为a - x*b,排序之后直接取前n-k大,判断这个值的正负性,然后按上面情况二分即可。
如果不大明白为什么是这么取得的,可以看这里(个人觉得很详细)
附上代码。
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #include<cmath> #include<set> #include<queue> #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #define enter putchar('\n') using namespace std; typedef long long ll; const int M = 10005; const int INF = 1000000009; const double eps = 1e-6; int read() { int ans = 0,op = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') op = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') { ans *= 10; ans += ch - '0'; ch = getchar(); } return ans * op; } struct test { double a,b,d; bool operator < (const test &g) const { return d > g.d; } }t[1005]; int n,k; double L,R,ans,mid; bool pd(double x) { double cur = 0; rep(i,1,n) t[i].d = t[i].a - x * t[i].b; sort(t+1,t+1+n); rep(i,1,n-k) cur += t[i].d; return cur >= 0; } int main() { while(1) { n = read(),k = read(); if(!n && !k) break; rep(i,1,n) t[i].a = read(); rep(i,1,n) t[i].b = read(); L = 0,R = 1e10; while(fabs(R - L) > eps) { mid = (L + R) / 2.0; if(pd(mid)) L = mid; else R = mid; } printf("%.0lf\n",(mid * 100.0)); } return 0; }