[USACO18OPEN]Talent Show(01分数规划)

版权声明:版权声明:本文为博主原创文章,未经博主允许不得转载,欢迎添加友链。 https://blog.csdn.net/zzk_233/article/details/83010978

这道题求得是所有的贡献值v和所有的重量w的最大比值。。但是正常的背包开不下,性质符不符合我也不确定。

即求\frac{\sum v[i]}{\sum w[i]}= ans,但是我们可以考虑每个点选与不选的状态,设这种状态为g[i],只有0和1两种状态,所以叫01分数规划。

这时状态就转为\sum v[i]*g[i]=ans*\sum w[i]*g[i],对于这个答案ans可以二分答案。

每次只要保证\sum v[i]*g[i]-ans*\sum w[i]*g[i]\geq 0,这样每次枚举到lim就好,不用枚举总重量。

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
int n,lim;
ll w[260],v[260],f[1000006];
ll check(ll x)
{
    for(int i=1;i<=lim;i++)
    {
        f[i]=-0x3f3f3f3f;
    }
    f[0]=0;
    for(int i=1;i<=n;i++)
    {
        for(int j=lim;j>=0;j--)
        {
            if(f[j]!=-0x3f3f3f3f)
            {
                f[min((ll)lim,(ll)j+w[i])]=max(f[min((ll)lim,(ll)j+w[i])],f[j]+v[i]-w[i]*x);
            }
        }
    }
    return f[lim]>=0;
}
ll findf()
{
    ll l=0,r=0x3f3f3f3f,mid;
    while(l<=r)
    {
        mid=(l+r)/2;
        if(check(mid))l=mid+1;
        else r=mid-1;
    }
    return r;
}
int main()
{
    scanf("%d%d",&n,&lim);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld%lld",&w[i],&v[i]);
        v[i]*=1000;
    }
    printf("%lld",findf());
    return 0;
}

猜你喜欢

转载自blog.csdn.net/zzk_233/article/details/83010978