Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

问题描述

\(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
//01分数规划 
#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;
}

给小狼留言