最大化平均值(二分答案应用)

今年有 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 ≤ kn。第二行有 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;
}

猜你喜欢

转载自blog.csdn.net/tsam123/article/details/89098008
今日推荐