牛客网暑期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 ]

思路

遇到这题之前还没听过01分数规划是个什么玩意,然后去学习了一波
01分数规划指的是有n个二元组 { v i , c i } ,然后选择其中的 k 的元素来得到 m a x ( v [ i ] c [ i ] ) 的一类问题
那怎么解决这类问题呢,我们可以用二分的方法来解决,来二分答案,为什么能二分呢,当前有

t = v [ i ] c [ i ]

其中 t 是我们假定的一个值,然后有 t c [ i ] = v [ i ] t c [ i ] v [ i ] = 0
如果是t是取k个元素中最优的答案,那么我们令 t [ i ] = t c [ i ] v [ i ] ,然后最 t 数组从小到大排序(因为我们的目标是和为0,和应该向0靠拢所以加前k个),那么前k个元素的和也就是0,但如果前k个元素的和大于0,这说明我们的答案偏大了,让二分的r=mid即可,反之和小于0让l=mid即可,这样就可以二分出答案,时间复杂度算上二分和排序为 O ( n l o g 2 n ) ,回到这题上来,这题和01分数规划的描述相同, v [ i ] = s [ i ] c [ 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;
}

猜你喜欢

转载自blog.csdn.net/ftx456789/article/details/81387262