码迷,mamicode.com
首页 > 其他好文 > 详细

[USACO18OPEN]Talent Show

时间:2019-02-13 09:17:14      阅读:176      评论:0      收藏:0      [点我收藏+]

标签:lse   为什么   lin   read   直接   答案   pre   ble   href   

传送门

题意: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\n",(int)(l*1000));
//按照题目要求输出
    return 0;
}

[USACO18OPEN]Talent Show

标签:lse   为什么   lin   read   直接   答案   pre   ble   href   

原文地址:https://www.cnblogs.com/PPXppx/p/10368009.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!