您的位置:首页 > 其它

2017年浙江工业大学大学生程序设计迎新赛预赛E 最大化平均值

2018-02-08 11:13 316 查看
链接:https://www.nowcoder.net/acm/contest/52/E

来源:牛客网

题目描述

小咪是一个土豪手办狂魔,这次他去了一家店,发现了好多好多(n个)手办,但他是一个很怪的人,每次只想买k个手办,而且他要让他花的每一分钱都物超所值,即:买下来的东西的总价值/总花费=max。请你来看看,他会买哪些东西吧。

输入描述:

多组数据。

第一行一个整数T,为数据组数。

接下来有T组数据。

对于每组数据,第一行两个正整数n,k,如题。

接下来n行,每行有两个正整数ci,vi。分别为手办的花费和它对于小咪的价值。

输出描述:

对于每组数据,输出一个数,即能得到的总价值/总花费的最大值。精确至整数。

示例1

输入

1

5 1

1 2

2 3

3 4

4 5

5 6

输出

2

备注:

1≤T≤10

1≤n≤104

1≤k≤n

1≤ci,vi≤104

大化平均值的经典,一般最先想到可能的方法是按照单位价值排序,从大到小的进行选取,但是这个方法对于下面一组例子来说:

n=3; k=2; (w,v)=(2,2),(5,3),(2,1);则可能得出的结果是5/7=0.714,所以这个方法是要排除的,那么如何想到最大化平均值这个方向呢?实际上,对于这个问题我们可以使用二分搜索法解决,我们定义:

条件get(mid):=可以选择使得单位重量的价值不小于mid

因此,原问题就变成了求满足get(mid)的最大mid,那么如何判断get(mid)是否可行?假设我们选了某个物品的集合S()那么它们的单位重量的价值是;定义sum(v)为v的值的和

sum(Vi(i属于S))/sum(Wi(i属于S));因此就变成了判断是否存在mid满足以下条件:sum(Vi(i属于S))/sum(Wi(i属于S))>=mid;变形得到:sum(Vi-mid*Wi)>=0,因此对此式的值进行贪心选取,进一步求sum(Vi-mid*Wi)从大到小排列中前k个的和不小于零,每次判断的复杂度为O(n*log(n))

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define N 10005
int w
,v
;
long long c
;
int check(int x);
int T,n,k,left,right,mid,ans;
int main()
{

scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&k);
for(int i = 1;i<=n;i++)
scanf("%d%d",&w[i],&v[i]);
int left = 0, right = 10000;// 注意left应从0开始,从1开始将会漏掉一些情况
while(left<=right)//注意二分法查找的条件必须有等于号
{
int mid = (left+right)/2;
if(check(mid))
{   ans = mid;
left = mid + 1;
}
else
right = mid -1;
}
printf("%d\n",ans);
}
return 0;

}
int check(int x)//条件C(x)为可以选择使得单位重量的价值不小于x,注意是大于或等于x,返回值为sum>=0
{
for(int i=1;i<=n;i++)
c[i] = v[i]-w[i]*x;//记住公式
sort(c+1,c+1+n);//正序排列
long long sum = 0;
for(int i=n;i>n-k;i--)//注意写法
sum +=c[i];
return sum>=0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐