[USACO18OPEN]Talent Show
Posted ppxppx
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[USACO18OPEN]Talent Show相关的知识,希望对你有一定的参考价值。
题意:N头牛,第i头牛重量wi,价值vi,选出总重量至少为W的牛,使得总价值与总重量的比值最大?
看到这种类似于一个物品有多个属性,且题目最后要求各属性总和比值最大的问题,就要想到分数规划.理解题意,我们要求的答案就是(frac{sum vi}{sum wi}),且(sum wi>=W)
我们直接二分比值mid(为什么可以二分,我在其它关于分数规划的题解里讲得比较清楚了),如果本次二分的值成立,即(frac{sum vi}{sum wi}>=mid),且(sum wi>=W)
整理一下上式得(sum (vi-wi*mid)>=0),且(sum wi>=W)
所以我们可以在每次check时,把每头奶牛的价值视作(vi-wi*mid),然后题目就转换为了,已知物品的价值和重量,选出总重量不小于W的物品,使价值和>=0.这不就类似于01背包问题?
只是在跑01背包的时候注意一个问题,一般的01背包问题都是背包容量不超过W,而本题物品总重量却是要求不小于W,所以我们可以把超过W的全都压在W里面,具体见代码实现.
int n,W,w[300],t[300];
double eps=1e-8,v[300],f[1005];
//因为是实数二分,随便设置了个精度eps
bool check(double mid){
for(int i=1;i<=W;i++)f[i]=-1e9;
//因为要求最大值,初值就设为负无穷.
for(int i=1;i<=n;i++)
v[i]=t[i]-w[i]*mid;
//每次check时根据mid更新每头奶牛的价值
for(int i=1;i<=n;i++)
for(int j=W;j>=0;j--){
if(w[i]+j>W)f[W]=max(f[W],f[j]+v[i]);
else f[w[i]+j]=max(f[w[i]+j],f[j]+v[i]);
}
//一般的01背包都是f[j]=max(f[j],f[j-w[i]+v[i])
//但因为我们选的物品总重量只有下限,没有上限,
//所以我们这里写作:如果没选这件物品,容量j就加上w[i]
//如果选了这件物品,直接总价值f[j]加上v[i]
//然后如果容量j+w[i]超过W,还是压在f[W]里面
return f[W]>=0;
//价值和>=0说明本次二分的mid合法
}
int main(){
n=read();W=read();
for(int i=1;i<=n;i++){
w[i]=read();
t[i]=read();
}
double l=0,r=1e9,mid;
while(l+eps<r){
mid=(l+r)/2.0;
if(check(mid))l=mid;
else r=mid;
}
printf("%d
",(int)(l*1000));
//按照题目要求输出
return 0;
}
以上是关于[USACO18OPEN]Talent Show的主要内容,如果未能解决你的问题,请参考以下文章
P4377 [USACO18OPEN]Talent Show
LG4377 「USACO2018OPEN」Talent Show 线性规划+背包
P4379 [USACO18OPEN]Lemonade Line