-
总时间限制:
- 1000ms 内存限制:
- 65536kB
-
描述
-
仙境的居民们决定举办一场程序设计区域赛。裁判委员会完全由自愿组成,他们承诺要组织一次史上最公正的比赛。他们决定将选手的电脑用星形拓扑结构连接在一起,即将它们全部连到一个单一的中心服务器。为了组织这个完全公正的比赛,裁判委员会主席提出要将所有选手的电脑等距离地围绕在服务器周围放置。
为购买网线,裁判委员会联系了当地的一个网络解决方案提供商,要求能够提供一定数量的等长网线。裁判委员会希望网线越长越好,这样选手们之间的距离可以尽可能远一些。
该公司的网线主管承接了这个任务。他知道库存中每条网线的长度(精确到厘米),并且只要告诉他所需的网线长度(精确到厘米),他都能够完成对网线的切割工作。但是,这次,所需的网线长度并不知道,这让网线主管不知所措。
你需要编写一个程序,帮助网线主管确定一个最长的网线长度,并且按此长度对库存中的网线进行切割,能够得到指定数量的网线。
输入
-
第一行包含两个整数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
来源
Northeastern Europe 2001
解题思路
一般像这种分治的题目,我们一般都要定义两个函数,一个函数使用来分治递归求最优解的,另一个函数就是用来判断这个解与题目要求的解的关系。
例如这道题目。首先我们来确定分治递归它的初始范围。左端点是0,但是取不到0;右端点应该是数据中网线最长的长度,为什么呢?应为如果题目要求我们割除一条最长的网线,我们肯定是从网线中挑出那根最长的网线使用,所以能够取到最长的网线长度。但是这里有一个问题,就是说分治递归一般不会取到端点值,那该怎么办呢?第一种方法,在每次递归的时候,判断右端点值是否满足题意;第二种方法,把递归的初始范围的右端点加上0.01,这样的话就可以取到最大值了。
首先我们第一一个cut函数,这个函数是用来切割网线的,他的参数是所要切割等长网线的长度,返回的参数是一共能切多少根这样的网线。这个函数挺好写。
第二个函数是分治递归函数,我们取区间的中点值,以这个值来切割网线。如果切割网线的数量小于了题目要求的k,就说明这个值偏大,把右端点置为mid,使之后的mid值变小;如果切割网线的数量大于了题目要求的k,说明这个值偏小,把左端点置为mid,使之后的mid值变大;如果切割网线的数量刚好的等于了题目要求的k,我们并不能直接返回这个mid,因为题目要求最长的等长网线,我们不能确定这个mid是否为满足题意中最大的mid,所以我们要把这种情况并在切割网线的数量大于了题目要求的k这种情况中,在(mid,r)中继续递归,看看能不能找到更小的满足题意的mid。
参考程序
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int n,k;
double w[10005],Max=-1,mid,m=-1;
int cut(double l)//以长度为l分割每一段绳子,返回分割段的总数
{
int ans=0;
for(int i=1;i<=n;i++)
ans+=int(w[i]/l);
return ans;
}
double dg(double l,double r)//分治递归
{
if(r-l<0.001)//如果左端点刚好满足或者右减左小于0.001(要使绳子的长度最大)
return r;
mid=(l+r)/2.0;
if(mid<0.01)//如果切的绳子的长度小于0.01,返回0
return 0;
if(cut(mid)>=k)//如果大于了需要的段数
return dg(mid,r);
if(cut(mid)<k)
return dg(l,mid);//如果小于了需要的段数
return mid;
}
int main()
{
cin>>n>>k;
for(int i=1;i<=n;i++)
{
cin>>w[i];
if(w[i]>m)//找出最大值
m=w[i];
}
printf("%.2lf",floor(dg(0,m+0.01)*100)/100.0);
return 0;
}