题目描述
有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