[USACO18OPEN]Talent Show

[USACO18OPEN]Talent Show

有N对数({w_i})({t_i}),选出几对数,使(w_i)之和大于W,并且使t之和除以w之和乘以1000尽可能大,(1≤N≤250,1≤W≤1000)

显然为分数规划,于是写出二分式(sum x_i(t_i-w_is)),现在问题在于如何选出括号里最大的数,并且使它们的(w_i)之和大于W,自然为一最优化问题,考虑递推,但是注意到题目很像背包,于是按照01背包的办法就可以求出最优解,注意边界所以要初始化无限小,其他照01分数规划的套路即可。

参考代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#define il inline
#define ri register
#define exact 0.000001
using namespace std;
int a[251],b[251],n,w;
double c[251],dp[1001];
il bool check(double);
il double dfs(double,double);
template<class free>il free Max(free,free);
int main(){
    int i;scanf("%d%d",&n,&w);
    for(i=1;i<=n;++i)
        scanf("%d%d",&b[i],&a[i]),a[i]*=1000;
    printf("%.0lf",dfs(0,250000)-0.5);
    return 0;
}
template<class free>
il free Max(free a,free b){
    return a>b?a:b;
}
il bool check(double x){
    int i,j;
    for(i=1;i<=w;++i)dp[i]=-1e19;
    for(i=1;i<=n;++i){
        c[i]=a[i]-b[i]*x;
        for(j=w;j>=w-b[i]&&j>=0;--j)
            dp[w]=Max(dp[w],dp[j]+c[i]);
        for(j=w;j>=b[i];--j)
            dp[j]=Max(dp[j-b[i]]+c[i],dp[j]);
    }return dp[w]>exact;
}
il double dfs(double l,double r){
    double mid;
    while(l+exact<r){
        mid=(l+r)/2;
        if(check(mid))l=mid+exact;
        else r=mid-exact;
    }return (l+r)/2;
}

原文地址:https://www.cnblogs.com/a1b3c7d9/p/10804880.html