【思维+二分】2018年东北农业大学春季校赛-I wyh的物品

wyh的物品

时间限制:C/C++ 5秒,其他语言10秒
空间限制:C/C++ 262144K,其他语言524288K

题目描述

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

说明

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

题意

给你n个物品,让你挑出k个,使其的单位价值最大。
再简单一点:最大化平均值。

思路

前提我们要知道的一个公式:

物品价值(b) = 单位价值(realm)*物品重量(a)

对于任意的单位价值m,我们都可以发现:
1、对于任意的一个物品,物品实际价值与对于任意单位价值的关系只有三种情况:

1、x = 物品实际价值(b) -  (任意单位价值(m)*物品重量(a)) == 0;
这说明:任意单位价值(m)==实际单位价值(realm)。
2、x = 物品实际价值(b) -  (任意单位价值(m)*物品重量(a)) > 0;
这说明:任意单位价值(m)<实际单位价值(realm)。
3、x = 物品实际价值(b) -  (任意单位价值(m)*物品重量(a)) < 0;
这说明:任意单位价值(m)>实际单位价值(realm)。

这里对应着核心代码:peizhong[i] = b[i] - ans*a[i];

2、我们将所有这些实际价值与单位价值之差分别存储到一个数组当中,为了保证任意单位价值最大,我们取前k大的差值(x)。
这里不太好想,我们换一个思路去思考,x值越大,就说明m值越小,就说明还有比m更大realm的值需要我们去找,我们的目的就是要找到更大的m值呀w。
这里对应着核心代码:sort(peizhong,peizhong+n,cmp);

3、我们把选择出的前k个差值相加,得到前k个差值最大的物品实际价值与物品对于任意单位价值(n)所求得的物品相对价值之差的之和(sum)。
这个值有两种情况

sum>=0 //任意的单位价值n取小了,导致实际价值还有剩余,还有更大的n。
sum<0  //任意的单位价值n取大了,导致实际价值都不可能满足由这个单位价值算出来的总价值。

4、使用二分进一步缩小n值的取值范围,直到满足我们规定的最小精度。

坑点

怎么样都想不出来【菜
容易想成背包问题、dp、暴力等等…..

AC代码

#include<bits/stdc++.h>
using namespace std;
int n,k;
typedef long long ll;

double a[100006],b[100006];
double peizhong[100006];

bool cmp(double a,double b)
{
    return a>b;
}

bool judge(double ans)
{
    /*核心的核心*/
    for(int i = 0 ; i < n ; i++){
        peizhong[i] = b[i] - ans*a[i];
    }
    /*贪心核心*/
    sort(peizhong,peizhong+n,cmp);
    double sum = 0;
    for(int i = 0 ; i < k ; i++){
        sum+=peizhong[i];

    }

    return sum>=0;
}

void solve(void)
{
    int t;
    cin>>t;
    while(t--)
    {
        cin>>n>>k;
        for(int i = 0 ; i < n ; i++){
            cin>>a[i]>>b[i];
        }
        double l = 0,r = 100000;
        double mid=0;
        /*二分查找核心*/
        while(r-l>=0.0001)
        {
            mid = (l+r)/2.0;
            if(judge(mid)){
                l = mid;
            }
            else r = mid;
        }
        printf("%.2f\n",mid);
    }
}



int main(void)
{
    solve();
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

两米长弦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值