今年有 n 场 ACM-ICPC 竞赛,小明每场都有资格参加。第 i 场竞赛共有 b[i] 道题。小明预测第 i 场他能做出 a[i] 道题。为了让自己看着更“大佬”一些,小明想让自己平均做出的题数越大越好,也就是最大化大佬度,大佬度的定义如下:
为了达到这个目的,小明决定放弃 k 场比赛的参赛资格。请求出最大的大佬度。
例如有 3 场小型比赛,题数分别是 5 题、1 题、6 题,小明预测自己分别能做出 5 题、0题、2题。如果每场都参加,那么大佬度是 ,看着不怎么大佬。不过,如果放弃第 3 场比赛,那么大佬度就是 ,看着更加大佬了。
Input
输入测试文件含有多组测试,每组有 3 行。第一行有 2 个整数, 1 ≤ n ≤ 1000 和 0 ≤ k< n。第二行有 n 个整数,即每个 a[i]。第三行含有 n 个正整数 b[i]。保证 0 ≤ a[i] ≤ b[i] ≤ 1, 000, 000, 000。文件末尾由 n = k = 0 标识,并且不应该被处理。
Output
对于每组测试数据,输出一行整数,即放弃 k 场比赛后可能的最高大佬度。大佬度应该舍入到最近的整数。
Sample Input
3 1 5 0 2 5 1 6 4 2 1 2 7 9 5 6 7 9 0 0
Sample Output
83 100
Hint
为了避免舍入误差带来的二义性,所有答案与除法边界相差至少 0.001 (例如答案永远不可能出现 83.4997)。
这道题目中文翻译的有一点问题,这个应该是将某K门课程的成绩降一些,使得平均值最大化,而不是上面所说的将一些课程去掉(有点坑),一开始完全不会怎么来做这道题目,看了看大神的代码,不由得长叹一声,(还有这种操作?还是自己太菜了,哎)。一开始就直接开始排序,然后再去取出元素算一下最大的平均值,但是细想这样是不对的啊,最后的结果不一定是最大的平均值。看看大神的思路,使用二分答案的方法来做:
那么我们这样分析这个问题:
令C(x)为可以选择使得单位重量的价值不小于x。
这样就可以用二分法来解决,不断二分x进行判断,取最大。
我们继续分析:
∑vi / ∑wi 这个式子是我们需要求的单位重量的价值。
i∈s i∈s
那么就求是否满足:
∑vi / ∑wi >= x
i∈s i∈s
转换一下得到:
∑(vi - x*wi) >= 0
i∈s
判断这个式子是否成立即可。这下就可以用一个数组来保存vi - x * wi 的值,并进行排序,从大到小贪心地进行选择求和,如果求和的值大于0,那么此时 的x就是成立的。 那个意思就是我们所求的平均值 要大于X,我们就一直二分这个X,那怎么来判断是不是合适呢?就是上面的那个式子 ∑(vi - x*wi) >= 0 来判断当前的答案适不适合,看看还能不能再大一点,如果太大了,那就小一点,到最后不能在二分的时候就是最大的时候了。
代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
const int maxn=1005;
typedef long long ll;
using namespace std;
const int inf=0x3f3f3f3f;
const double eps=1e-7;
ll a[maxn]; //做的
ll b[maxn]; //总的
double c[maxn];
ll n,k;
bool cmp(double a,double b)
{
return a>b;
}
bool check(double mid)
{
for(int i=0;i<n;i++)
c[i]=a[i]-mid*b[i];
sort(c,c+n,cmp);
double sum=0;
for(int i=0;i<n-k;i++)
sum+=c[i];
return sum>=0.0;
}
int main()
{
while(scanf("%lld%lld",&n,&k)!=EOF)
{
if(n==0&&k==0)
break;
for(int i=0;i<n;i++)
scanf("%lld",&a[i]);
for(int i=0;i<n;i++)
scanf("%lld",&b[i]);
double left=0;
double right=inf;
while(right-left>eps)
{
double mid=(left+right)/2.0;
if(check(mid))
left=mid;
else
right=mid;
}
printf("%.0f\n",left*100.0);
}
return 0;
}