二分答案-洛谷P1577 切绳子

题目描述
有N条绳子,它们的长度分别为Li。如果从它们中切割出K条长度相同的绳子,这K条绳子每条最长能有多长?答案保留到小数点后2位(直接舍掉2为后的小数)。

输入格式
第一行两个整数N和K,接下来N行,描述了每条绳子的长度Li。

输出格式
切割后每条绳子的最大长度。

之前的比赛出过这个题,当时没看出来是二分答案,正好之前也没怎么练习过,今天重新做了一遍。
此题涉及精度问题 坑比较多

先来看解法一

担心最后因为精度造成最小值过小,提前加L[i] += 1e-5;这个操作

#include <iostream>
#include <cmath>
#include <cstdio>
using namespace std;
int n, k;
double L[10005];
double eps = 1e-9;//判断左区间和右区间的差是否为一个极小值
bool f(double x)
{
	int num = 0;
	for (int i = 0; i < n; i++)
	{
		num += (int)(L[i] / x);
	}
	return num >= k;//目前找到的个数比答案要多,就返回true
}
int main()
{
	cin >> n >> k;
	for (int i = 0; i < n; i++)
	{
		cin >> L[i];
		L[i] += 1e-5;
	}
	double l = 0, r = 0x7fffffff;
	while (fabs(l - r) > eps)
	//for (int i = 1; i <= 100; i++)
	{
		double mid = (l + r) / 2;
		if (f(mid))//目前找到的个数比答案要多,那么证明现在这个值选小了,需要查右半部分
		{
			l = mid;
		}
		else
		{
			r = mid;
		}
		//cout << l << " " << mid << " " << r << endl;
		//printf("%.10lf %.10lf %.10lf\n", l, mid, r);
	}
	printf("%.2lf\n", floor(l*100)/100);
}

再来看解法二

不进行初始化操作也过了。。。

但是坑的是这个测试点

1 10
100.00

把r初始化成1e9就不对,按理说10.00不是在1e9这个范围内?不是都可以覆盖到这个答案?把r换成1e9会输出9.99 一直想不通这个问题,欢迎大佬指导

#include <iostream>
#include <cmath>
#include <cstdio>
using namespace std;
int n, k;
double L[10005];
double eps = 1e-9;//判断左区间和右区间的差是否为一个极小值
bool f(double x)
{
	int num = 0;
	for (int i = 0; i < n; i++)
	{
		num += (int)(L[i] / x);
	}
	return num >= k;//目前找到的个数比答案要多,就返回true
}
int main()
{
	cin >> n >> k;
	for (int i = 0; i < n; i++)
	{
		cin >> L[i];
		//L[i] += 1e-5;
	}
	double l = 0, r = 0x7fffffff;
	while (fabs(l - r) > eps)
	//for (int i = 1; i <= 100; i++)
	{
		double mid = (l + r) / 2;
		if (f(mid))//目前找到的个数比答案要多,那么证明现在这个值选小了,需要查右半部分
		{
			l = mid;
		}
		else
		{
			r = mid;
		}
		//cout << l << " " << mid << " " << r << endl;
		//printf("%.10lf %.10lf %.10lf\n", l, mid, r);
	}
	printf("%.2lf\n", floor(l*100)/100);
}

解法三

据说这个控制精度可以在1e-30?

#include <iostream>
#include <cmath>
#include <cstdio>
using namespace std;
int n, k;
double L[10005];
double eps = 1e-9;//判断左区间和右区间的差是否为一个极小值
bool f(double x)
{
	int num = 0;
	for (int i = 0; i < n; i++)
	{
		num += (int)(L[i] / x);
	}
	return num >= k;//目前找到的个数比答案要多,就返回true
}
int main()
{
	cin >> n >> k;
	for (int i = 0; i < n; i++)
	{
		cin >> L[i];
		//L[i] += 1e-5;
	}
	double l = 0, r = 0x7fffffff;
	//while (fabs(l - r) > eps)
	for (int i = 1; i <= 100; i++)
	{
		double mid = (l + r) / 2;
		if (f(mid))//目前找到的个数比答案要多,那么证明现在这个值选小了,需要查右半部分
		{
			l = mid;
		}
		else
		{
			r = mid;
		}
		//cout << l << " " << mid << " " << r << endl;
		//printf("%.10lf %.10lf %.10lf\n", l, mid, r);
	}
	printf("%.2lf\n", floor(l*100)/100);
}

拓展

floor()

这个东西可以理解为小数的向下取整

floor(2.6) = 2
floor(2.2) = 2
floor(-2.6) = -3
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值