牛客网暑期ACM多校训练营(第五场)A.gpa (01分数规划)

链接:https://www.nowcoder.com/acm/contest/143/A
来源:牛客网

题目描述
Kanade selected n courses in the university. The academic credit of the i-th course is s[i] and the score of the i-th course is c[i].
At the university where she attended, the final score of her is 这里写图片描述
Now she can delete at most k courses and she want to know what the highest final score that can get.
输入描述:
The first line has two positive integers n,k

The second line has n positive integers s[i]

The third line has n positive integers c[i]
输出描述:
Output the highest final score, your answer is correct if and only if the absolute error with the standard answer is no more than 10-5
示例1
输入

复制
3 1
1 2 3
3 2 1
输出

复制
2.33333333333

题意

给你n个课程的学分和绩点,然后你可以去掉其中的k个课程的学分和绩点,然后求最大的平均学分绩点 ∑ s [ i ] ∗ c [ i ] ∑ s [ i ] \frac{\sum s[i]*c[i]}{\sum s[i]} s[i]s[i]c[i]

思路

遇到这题之前还没听过01分数规划是个什么玩意,然后去学习了一波
01分数规划指的是有n个二元组 { v i , c i } \{ v_i,c_i \} {vi,ci},然后选择其中的 k k k的元素来得到 m a x ( ∑ v [ i ] ∑ c [ i ] ) max(\frac{\sum v[i]}{\sum c[i]}) max(c[i]v[i])的一类问题
那怎么解决这类问题呢,我们可以用二分的方法来解决,来二分答案,为什么能二分呢,当前有
t = ∑ v [ i ] ∑ c [ i ] t=\frac{\sum v[i]}{\sum c[i]} t=c[i]v[i]
其中 t t t是我们假定的一个值,然后有 t ∑ c [ i ] = ∑ v [ i ] t\sum c[i]=\sum v[i] tc[i]=v[i] t ∑ c [ i ] − ∑ v [ i ] = 0 t\sum c[i]-\sum v[i]=0 tc[i]v[i]=0
如果是t是取k个元素中最优的答案,那么我们令 t [ i ] = t ∗ c [ i ] − v [ i ] t[i]=t*c[i]-v[i] t[i]=tc[i]v[i],然后最 t t t数组从小到大排序(因为我们的目标是和为0,和应该向0靠拢所以加前k个),那么前k个元素的和也就是0,但如果前k个元素的和大于0,这说明我们的答案偏大了,让二分的r=mid即可,反之和小于0让l=mid即可,这样就可以二分出答案,时间复杂度算上二分和排序为 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n),回到这题上来,这题和01分数规划的描述相同, v [ i ] = s [ i ] ∗ c [ i ] v[i]=s[i]*c[i] v[i]=s[i]c[i] c [ i ] = s [ i ] c[i]=s[i] c[i]=s[i],往上套二分即可

#include <iostream>
#include <string>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#include <math.h>
using namespace std;
const int N=1e5+5;
double a[N],b[N];
double t[N];
int main()
{
    int n,k;
    while(scanf("%d%d",&n,&k)!=EOF)
    {
        for(int i=0;i<n;i++)
            scanf("%lf",&b[i]);
        double r=0;
        for(int i=0;i<n;i++)
        {
            scanf("%lf",&a[i]);
            if(r<a[i])
                r=a[i];
            a[i]*=b[i];
        }
        double l=0;
        double mid;
        while(fabs(l-r)>1e-9)
        {
            mid=(l+r)/2;
            for(int i=0;i<n;i++) t[i]=mid*b[i]-a[i];
            sort(t,t+n);
            double sum=0;
            for(int i=0;i<n-k;i++)
                sum+=t[i];
            if(sum<0)
                l=mid;
            else
                r=mid;
        }
        printf("%.9lf\n",mid);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值