2018年东北农业大学春季校赛 I-wyh的物品

2018年东北农业大学春季校赛 I-wyh的物品

链接:https://www.nowcoder.com/acm/contest/93/I

来源:牛客网

题目描述

wyh学长现在手里有n个物品,这n个物品的重量和价值都告诉你,然后现在让你从中选取k个,问你在所有可能选取的方案中,最大的单位价值为多少(单位价值为选取的k个物品的总价值和总重量的比值)

输入描述:

输入第一行一个整数T(1<=T<=10)

接下来有T组测试数据,对于每组测试数据,第一行输入两个数n和k(1<=k<=n<=100000)

接下来有n行,每行两个是a和b,代表这个物品的重量和价值

输出描述:

对于每组测试数据,输出对应答案,结果保留两位小数

示例1

输入

1
3 2
2 2
5 3
2 1

输出

0.75

说明

对于样例来说,我们选择第一个物品和第三个物品,达到最优目的

思路

咋眼一看(其实看了也很久),以为是背包(一直往背包方向上靠)。

但其实这是求一个最大化的平均值(使用二分法)。

这可能有点难理解。我们不妨把(目标要求的)单位价值设为x,差值为y。

y = 重量 * x - 价值

为了保证x最大,我们取y值前k大的(重量,价值)对。

对于每组(重量,价值)对,逐一计算出y值,然后递增排列,取前k个计算得出(总质量,总价值)。

使用二分寻找出目标x,对应差值y应接近于0:

y = 总重量 * x - 总价值 (y → 0)

使用二分缩小x取值范围,直到满足规定的最小精度。

AC代码

#include <iostream>
#include <vector>
#include <algorithm>
#include <iomanip>
using namespace std;

struct things {
    double a;   // 重量
    double b;   // 价值
};
double low,mid,high;
bool cmp(things fir, things sec) {
    return mid * fir.a - fir.b < mid * sec.a - sec.b;
}

int main() {
    int T;
    cin >> T;
    while(T--) {
        int n,k;
        cin >> n >> k;
        vector<things> vec;
        for(int i=0; i<n; i++) {
            double a,b;
            cin >> a >> b;
            things th;
            th.a = a;
            th.b = b;
            vec.push_back(th);
        }
        double value, weight, ans;
        double res; // 差值
        low = 0, high = 1000,ans = 0;
        while(high-low > 1e-6) {
            mid = (low + high) / 2.0;
            sort(vec.begin(), vec.end(), cmp);
            value = weight = 0;
            for(int i = 0; i < k; i++) {
                things tmp = vec.at(i);
                value += tmp.b;
                weight += tmp.a;
            }
            ans = value / weight;
            res = mid * weight - value;
            if(res < 0)
                low = mid;
            else
                high = mid;
        }
        //cout << mid << endl;
        cout << fixed << setprecision(2) << ans << endl;
    }

    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值