nyoj 914 Yougth的最大化

来源:互联网 发布:ubuntu显示隐藏文件 编辑:程序博客网 时间:2024/06/05 07:06

原题:  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;}



原创粉丝点击