首页 > 代码库 > 二分查值,正确的姿势

二分查值,正确的姿势

04:网线主管

总时间限制: 
1000ms
 
内存限制: 
65536kB
描述

仙境的居民们决定举办一场程序设计区域赛。裁判委员会完全由自愿组成,他们承诺要组织一次史上最公正的比赛。他们决定将选手的电脑用星形拓扑结构连接在一起,即将它们全部连到一个单一的中心服务器。为了组织这个完全公正的比赛,裁判委员会主席提出要将所有选手的电脑等距离地围绕在服务器周围放置。

为购买网线,裁判委员会联系了当地的一个网络解决方案提供商,要求能够提供一定数量的等长网线。裁判委员会希望网线越长越好,这样选手们之间的距离可以尽可能远一些。

该公司的网线主管承接了这个任务。他知道库存中每条网线的长度(精确到厘米),并且只要告诉他所需的网线长度(精确到厘米),他都能够完成对网线的切割工作。但是,这次,所需的网线长度并不知道,这让网线主管不知所措。

你需要编写一个程序,帮助网线主管确定一个最长的网线长度,并且按此长度对库存中的网线进行切割,能够得到指定数量的网线。

输入
第一行包含两个整数N和K,以单个空格隔开。N(1 <= N <= 10000)是库存中的网线数,K(1 <= K <= 10000)是需要的网线数量。
接下来N行,每行一个数,为库存中每条网线的长度(单位:米)。所有网线的长度至少1m,至多100km。输入中的所有长度都精确到厘米,即保留到小数点后两位。
输出
网线主管能够从库存的网线中切出指定数量的网线的最长长度(单位:米)。必须精确到厘米,即保留到小数点后两位。
若无法得到长度至少为1cm的指定数量的网线,则必须输出“0.00”(不包含引号)。
样例输入
4 118.027.434.575.39
样例输出
2.00

思路:
  避免精度问题,把单位全部*100换算成厘米,最后/100.0转成double。从最小的1厘米开始二分到Max,最大只能分到Max长度。
  ans是取最大值,所以在左边界更新的时候更新。
  一开始的用的求最后一个区间值得二分姿势,卡边界,只能得8到9分,边界开到1000w+10才能得10分。

有问题的代码,虽然AC了,但是其实是开足了边界:
比如 1 10,找的是10,最后卡在9,10,这时候l=r-1,ans=l=9就退出二分了,这种二分姿势是第一道题的姿势。。这里明显是不对的
#include <iostream>#include <cstdio>#include <cmath>#include <bits/stdc++.h>const int maxn = 10000+10;using namespace std;int a[maxn];int ans,n,k;int check(int m) {    int cnt = 0;    for(int i = 1; i <= n; i++) {        cnt += a[i]/m;    }    return cnt;    }void search(int l,int r) {    int mid;    while(l < r-1) {        mid = (l+r)/2;        if(check(mid)<k) {            r = mid;        }        else if(check(mid)>=k) {            ans = mid;            l = mid;        }    }    printf("%.2f\n",ans/100.0);    }int main(){//    freopen("in.txt","r",stdin);    scanf("%d%d",&n,&k);    int Max = -1;    for(int i = 1; i <= n; i++) {        double t;        scanf("%lf",&t);        a[i] = (int)(t*100);        if(a[i]>Max) {            Max = a[i];        }    }    sort(a+1,a+1+n);    search(1,10000010);    return 0;}

 

正确的姿势:

l<=r,利用左加1,右+1来偏移二分遍历所有情况最后退出条件。

注意这里的最低精度就是1厘米,如果不满住是进不了循环,ans默认值全局变量是0,/100.0隐式转换成double输出的是0.00

#include <iostream>#include <cstdio>#include <cmath>#include <bits/stdc++.h>const int maxn = 10000+10;using namespace std;int a[maxn];int ans,n,k;int check(int m) {    int cnt = 0;    for(int i = 1; i <= n; i++) {        cnt += a[i]/m;    }    return cnt;    }void search(int l,int r) {    int mid;    while(l <= r) {        mid = (l+r)/2;        if(check(mid)<k) {            r = mid-1;        }        else if(check(mid)>=k) {            ans = mid;            l = mid+1;        }    }    printf("%.2f\n",ans/100.0);    }int main(){//    freopen("in.txt","r",stdin);    scanf("%d%d",&n,&k);    int Max = -1;    for(int i = 1; i <= n; i++) {        double t;        scanf("%lf",&t);        a[i] = (int)(t*100);        if(a[i]>Max) {            Max = a[i];        }    }    sort(a+1,a+1+n);    search(1,Max);    return 0;}

 

二分查值,正确的姿势