问题描述
有 \(n\) 个物品,每种物品两个权值
\(a_i\),\(b_i\),求一组 \(w_i\in\{0,1\}\),使得
\[
\frac{\sum_{i=1}^n w_i\cdot a_i}{\sum_{i=1}^n w_i\cdot b_i}
\]
最大(或最小)。
有可能包含其他限制。
二分法
显然答案是单调的。对一个答案 \(mid\),有
\[
\begin{aligned}
&\frac{\sum_{i=1}^n w_i\cdot a_i}{\sum_{i=1}^n w_i\cdot
b_i}>mid\\
\Leftrightarrow&\sum_{i=1}^n w_i\cdot a_i-mid\sum_{i=1}^n w_i\cdot
b_i>0\\
\Leftrightarrow&\sum_{i=1}^n w_i\cdot (a_i-mid \cdot b_i)>0\\
\end{aligned}
\]
因此可以对该权值 \(a_i-mid \cdot
b_i\) 进行贪心对答案进行检查。顺便满足题目要求。
例题
POJ2976
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| #include<iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; int n,k; int a[1010],b[1010]; double d[1010]; const double eps=1e-7; inline bool ok(double x) { for(int i=1;i<=n;i++) d[i]=a[i]-x*b[i]; sort(d+1,d+1+n); double ans=0; for(int i=n;i>=k+1;i--)ans+=d[i]; return ans>=0; } int main() { while(scanf("%d%d",&n,&k) and n|k) { for(int i=1;i<=n;i++)scanf("%d",a+i); for(int i=1;i<=n;i++)scanf("%d",b+i); double l=0,r=1; while(r-l>eps) { double mid=(l+r)/2; if(ok(mid))l=mid; else r=mid; } printf("%.0f\n",l*100); } return 0; }
|