1242:网线主管
题目
1242:网线主管
【题目描述】
仙境的居民们决定举办一场程序设计区域赛。裁判委员会完全由自愿组成,他们承诺要组织一次史上最公正的比赛。他们决定将选手的电脑用星形拓扑结构连接在一起,即将它们全部连到一个单一的中心服务器。为了组织这个完全公正的比赛,裁判委员会主席提出要将所有选手的电脑等距离地围绕在服务器周围放置。 为购买网线,裁判委员会联系了当地的一个网络解决方案提供商,要求能够提供一定数量的等长网线。裁判委员会希望网线越长越好,这样选手们之间的距离可以尽可能远一些。
该公司的网线主管承接了这个任务。他知道库存中每条网线的长度(精确到厘米),并且只要告诉他所需的网线长度(精确到厘米),他都能够完成对网线的切割工作。但是,这次,所需的网线长度并不知道,这让网线主管不知所措。
你需要编写一个程序,帮助网线主管确定一个最长的网线长度,并且按此长度对库存中的网线进行切割,能够得到指定数量的网线。
【输入】
第一行包含两个整数N和K,以单个空格隔开。N(1 ≤ N ≤ 10000)是库存中的网线数,K(1 ≤ K ≤ 10000)是需要的网线数量。
接下来N行,每行一个数,为库存中每条网线的长度(单位:米)。所有网线的长度至少1m,至多100km。输入中的所有长度都精确到厘米,即保留到小数点后两位。
【输出】
网线主管能够从库存的网线中切出指定数量的网线的最长长度(单位:米)。必须精确到厘米,即保留到小数点后两位。
若无法得到长度至少为1cm的指定数量的网线,则必须输出“0.00”(不包含引号)。
【输入样例】
4 11
8.02
7.43
4.57
5.39
【输出样例】
2.00
【分析】
这个题目其实不太难想的,因为:从数据规模来看,n<=10000,k<=10000,每条线的最大长度100km,换成厘米10^8,怎么看数据还是可以接受的。时间复杂度可以做到O(n2)级,这个时间复杂度对大多题目都够的;方法嘛,应该可以想到二分答案值。最小1cm,最大100km,精确到cm,那就是二分了。
但是:这个题却让我修改了多次。(可能是我笨)。这里面有几个小问题需要考虑清楚。1、我第一次做出的答案样例都没过,我给出的答案是2.01,细查答案的实际值为2.005(可以手动算的,这个值没毛病),保留两位小数自然就2.01了。带给我的思考是:尾数如何收舍才合理?显然四舍五入是行不通的。去尾法也让人举棋不定,我们还得考虑另外一个问题,数值类型一般选用double,就算是2.00在内部存储也可能是1.99999999999997,你来个去尾必须被K.O。解决这个问题好像也不太难,答案+0.000001再去尾。那么恭喜你,你可能能得到80分。2、注意到题目中多次提到各方面数据都精确到cm,那好,我们直接以cm为单位,那就全是整数了。这个想法让我也是一阵兴奋,马上动手修改,抛掉那个尾数处理的烦恼。结果是:70分。3、应该说问题不大啊,怎么会呢?再细查一遍,好像都没问题啊。终于,发现一个漏洞:以cm为单位,最长100km,那就是10^8,要是多来几个100km,得到的网线段数......,我去,还有这事儿,有了刚才的打击,这次不敢高兴,只有试试的态度了,改int为long long,嘿,还真给力,问题解决。好吧,不讲故事了,翠花,上酸菜。
AC代码
//1242:网线主管 #include<iostream> #include<iomanip> using namespace std; int const N=1e4+1; long long b[N],ans,n,s; double t; long long ts(int x) { long long int num=0; for(int i=1;i<=n;i++)num+=b[i]/x; return num; } int f(int l,int r) { int m=(l+r)/2; if(r-l<=1)return l; if(ts(m)>=s)return f(m,r); else return f(l,m); } int main(){ cin>>n>>s; for(int i=1;i<=n;i++) { cin>>t; b[i]=t*100+0.1; } if(ts(1)<s)ans=0; else if(ts(1e8)>=s)ans=1e8; else ans=f(1,1e8); cout<<fixed<<setprecision(2)<<ans/100.0+0.0001; return 0; }