BZOJ5281 [Usaco2018 Open]Talent Show [0/1分数规划, 背包]

Talent ShowTalent Show

题目描述见链接 .


color{red}{正解部分}

0/10/1 分数规划,

二分出答案 midmid, 要满足 tiwimidmid×witi0(mid×witi)0frac{sum t_i}{sum w_i} le mid ightarrow mid imes sum w_i - sum t_i geq 0 ightarrow sumleft( mid imes w_i - t_i ight) geq 0 .

由于题目有总重量不小于 WW 的限制, 所以不能直接贪心选,

考虑 背包, 设 F[i]F[i] 表示总重量为 ii 所能获得的最大值, 由于 WW 较小, 而 wiw_i 比较大,
所以考虑将 F[xW]F[x geq W] 全部存在 F[W]F[W] 中, 做 0/10/1 背包 即可 .


color{red}{实现部分}

#include<bits/stdc++.h>
#define reg register

const int maxn = 1005;
const double inf = 1e10;
const double eps = 1e-9;

int N;
int W;
int w[maxn];
int t[maxn];

double F[maxn];

bool check(double mid){
        for(reg int i = 1; i <= W; i ++) F[i] = -inf;
        F[0] = 0;
        for(reg int i = 1; i <= N; i ++)
                for(reg int j = W; j >= 0; j --){
                        double &tmp = F[std::min(W, j+w[i])];
                        tmp = std::max(tmp, F[j] + 1.0*t[i] - 1.0*mid*w[i]);
                }
        return F[W] >= 0;
}

int main(){
        scanf("%d%d", &N, &W);
        double l = 0, r = 0;
        for(reg int i = 1; i <= N; i ++) scanf("%d%d", &w[i], &t[i]), r += t[i];
        double Ans = 0;
        while(r-l > eps){
                double mid = (l+r)/2;
                if(check(mid)) Ans = std::max(Ans, mid), l = mid + eps;
                else r = mid - eps;
        }
        printf("%d
", (int)(Ans*1000));
        return 0;
}
原文地址:https://www.cnblogs.com/zbr162/p/11822404.html