P4377 [USACO18OPEN] Talent Show G
有 n n n头奶牛,第 i i i头奶牛的重量为 w i w_i wi,才艺水平为 t i t_i ti。你需要选择若干头奶牛,使得
这些奶牛的总重量大于等于 W W W,求选择若干头奶牛能够达到的才艺与重量的比值的最大值,并输出这个最大值成一千再向下取整后的值。
1 ≤ n ≤ 250 , 1 ≤ W ≤ 1000 , 1 ≤ w i ≤ 1 0 6 , 1 ≤ t i ≤ 1 0 3 1\leq n\leq 250,1\leq W\leq 1000,1\leq w_i\leq 10^6,1\leq t_i\leq 10^3 1≤n≤250,1≤W≤1000,1≤wi≤106,1≤ti≤103
二分答案,设当前二分的值为 m i d mid mid,则
∑ t i ∑ w i ≥ m i d \dfrac{\sum t_i}{\sum w_i}\geq mid ∑wi∑ti≥mid
由此可得
∑ t i ≥ m i d × ∑ w i \sum t_i\geq mid\times \sum w_i ∑ti≥mid×∑wi
移项得
∑ ( t i − m i d × w i ) ≥ 0 \sum(t_i-mid\times w_i)\geq 0 ∑(ti−mid×wi)≥0
令 v i = t i − m i d × w i v_i=t_i-mid\times w_i vi=ti−mid×wi,那么每头奶牛的重量为 w i w_i wi,价值为 w i w_i wi,用背包即可解决。重量大于等于 W W W的都记录在 f W f_W fW中。最后只需判断 f W f_W fW是否大于 0 0 0即可。
为了方便,所有 t t t值在一开始就乘了 1000 1000 1000。
时间复杂度为 O ( n w log S ) O(nw\log S) O(nwlogS),其中 S = ∑ w i S=\sum w_i S=∑wi。
#include
using namespace std;
int n,w;
long long f[305][1005];
struct node{
int w,t;
}v[305];
bool check(long long k){
for(int i=0;i<=n;i++){
for(int j=0;j<=w;j++) f[i][j]=-1e15;
}
f[0][0]=0;
for(int i=1;i<=n;i++){
for(int j=0;j<=w;j++) f[i][j]=f[i-1][j];
for(int j=0;j<=w;j++){
f[i][min(j+v[i].w,w)]=max(f[i][min(j+v[i].w,w)],f[i-1][j]+v[i].t-k*v[i].w);
}
}
return f[n][w]>=0;
}
int main()
{
scanf("%d%d",&n,&w);
for(int i=1;i<=n;i++){
scanf("%d%d",&v[i].w,&v[i].t);
v[i].t*=1000;
}
int l=0,r=3e8,mid;
while(l<=r){
mid=l+r>>1;
if(check(mid)) l=mid+1;
else r=mid-1;
}
printf("%d",l-1);
return 0;
}