nyoj 914 Yougth的最大化

原题:  http://acm.nyist.net/JudgeOnline/problem.php?pid=914

//nyoj 914  二分+巧妙的贪心 
//思路:因为题目限定选k个物品值是固定的,而结果rs的范围我们也是可以知道的,即0<rs<=m(m为单位价值最大的的某个物品),所以采用二分法,每一轮判断中间值mid的合理性来缩小范围 
//     如何判定mid的合法性? 这是这题的难点,也是最巧妙的一点
//     假设单价为mid, W为所选的 k个物品的总质量, w1~wk为各个物品的重量,V1~Vk为各物品的总价值
//     则数学计算得到公式  mid <= (V1+V2+...Vk)/W  满足即为合法,公式等价转化为 
//     ---->   mid * W <= V1+V2+...Vk
//     ---->   mid  *(w1 +w2+....+wk) <= V1+V2+...+Vk  
//     ---->  (V1+V2+...+Vk) - (mid*w1 + mid *w2+...+mid*wk) >=0      
//     ---->  (V1-mid*w1) + (V2-mid*w2) +... +(Vk-mid*wk) >=0      最终式子① 
//     所以说问题转化为 对于给定的mid,如果能找到k个物品,如果他们满足上面这个 式①这个mid就是合理的。 
//     这时候就可以贪心了,用一个数组arr存放每个物品 mid*wi-Vi的结果,然后从大到小排序,看前k个相加能否>=0能即为合理,返回1,否则则返回0 
//小细节:这里为了方便处理精度问题,我把结果进行了乘100处理,如果不理解可以忽略我处理精度的地方。
//这题之前做过,但是WA了很多次,没有做出来。也是看了别人的思路才做出来的。现在自己做总结,希望加深印象,对二分和贪心有更深刻的认识。 
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
struct M
{
	double wi;
	double vi;
}m[10001];
int n,k; //  n个物品,选k个
double max(double a,double b) 
{
	if(a>=b)return a;
	return b;
}
int cmp(const void * a,const void * b) //快排,从大到小排序 
{
	return *(double *)a < *(double *)b;
}
int greed(double per) //检测per的合法性 
{
	per=per/100;    
	double arr[10001]; 
	for(int j=0;j<n;j++)
	{
		arr[j]=m[j].vi- per*m[j].wi; //记录 Vi-mid*wi
	}
	qsort(arr,n,sizeof(arr[0]),cmp);//对 Vi-mid*wi 的结果排序  
	double sum=0;
	for(int j=0;j<k;j++)  //前k个相加 
	{
		sum=sum+arr[j];
	}
	if(sum<0)return 0;//小于0,不满足式①,不合法,返回0 
	return 1;//大于等于,返回1 
}
int main()
{
	while(~scanf("%d %d",&n,&k))
	{
		int i;//临时变量 
		double r=-1;  //区间右边界 
		for(i=0;i<n;i++)
		{
			scanf("%lf %lf",&m[i].wi,&m[i].vi);
			r=max(r,m[i].vi/m[i].wi); //找到单价最高的那个物品 
		}
		double l=0; //区间左边界 
		r=r*100; //精度处理 
		double rs=0; //存放结果 
		while(l<=r)
		{
			double mid=(l+r)/2;
			if(greed(mid)){ //如果合理 
				rs=mid;    //暂存mid值 
				l=mid+0.01; //并增大l值 
			}else{  //如果不合理 
				r=mid-0.01;  //减小r值 
			}
		}
		printf("%.2lf\n",rs/100);
	}
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值