题目描述
有 N 根绳子,第 i 根绳子长度为 Li,现在需要 M 根等长的绳子,你可以对 N 根绳子进行任意裁剪(不能拼接),请你帮忙计算出这 M 根绳子最长的长度是多少。
输入格式
第一行包含 2 个正整数 N, M,表示原始绳子的数量和需求绳子的数量。
第二行包含 N个整数,其中第 i 个整数 Li 表示第 i 根绳子的长度。
输出格式
输出一个数字,表示裁剪后最长的长度,保留两位小数。
样例
输入数据 1
3 4
3 5 4
输出数据 1
2.50
数据范围
1≤N,M≤100000
0<Li<10的9次方
解题思路
对n根绳子裁剪成m根等长的绳子,易知m>=n,我们可以采用二分逼近法求取它最长的长度,因为每根绳子的最大长度为10的9次方,所以我们需取的最长长度范围为0~10的9次方,然后用一个函数判断n根绳子可以截出多少根等长的绳子长度(必须用每根绳子mod所取长度,因为n根绳子长度mod所取长度的话会出现多的情况),判断如果大于或等于m的话就取右半区域,小于取左半区域。
求总的绳子能分出多少段:
bool check(double x){
int ans=0;
for(int i=1;i<=n;i++)//求每根绳子能分出x有多少段
ans+=a[i]/x;
return ans>=m;//总的段数
}
二分取所需最长长度:
double l=0,r=1e9;//每根绳子的最大数据是10的九次方
while(r>l+1e-3)//如果单纯r>l的话,因为是浮点数,一直取半到很多的小数点后面,l永远不小于r,而裁剪后的长度保留两位数所以r>l+10的-3次方
{
double mid=(l+r)/2;//二分法
if(check(mid))//满足的话取后半区
l=mid;//一直逼近m根绳子取最长的长度
else
r=mid;//取太大了就往小了找
}
完整代码:
#include<iostream>
#include<cstdio>
#define MAXN 100005
using namespace std;
int a[MAXN];
int n,m;
bool check(double x){
int ans=0;
for(int i=1;i<=n;i++)//求每根绳子能分出x有多少段
ans+=a[i]/x;
return ans>=m;//总的段数
}
int main()
{
cin >> n >> m;
for(int i=1;i<=n;i++)
cin >> a[i];
double l=0,r=1e9;//每根绳子的最大数据是10的九次方
while(r>l+1e-3)//如果单纯r>l的话,因为是浮点数,一直取半到很多的小数点后面,l永远不小于r,而裁剪后的长度保留两位数所以r>l+10的-3次方
{
double mid=(l+r)/2;//二分法
if(check(mid))//满足的话取后半区
l=mid;//一直逼近m根绳子取最长的长度
else
r=mid;//取太大了就往小了找
}
printf("%.2f\n",l);//输出保留两位小数
return 0;
}
值得注意的是浮点数二分法不能向整型二分法一样结束条件为l=<r,浮点数每次二分都会取到小数点后面的位数,所以while里面可能会一直循环,这里因为我们只需保留两位小数,所以我们只要判断l+10的-3次方<r即可。
注:如有错,请大佬指出,谢谢!