剪绳子(3月27日)

题目描述

有 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即可。

注:如有错,请大佬指出,谢谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值