这里主要拿题目790. 数的三次方根作为例题进行讲解:
题目:790. 数的三次方根
题目描述:
给定一个浮点数 n,求它的三次方根。
输入格式
共一行,包含一个浮点数 n。
输出格式
共一行,包含一个浮点数,表示问题的解。
注意,结果保留 6 位小数。
数据范围
−
10000
≤
n
≤
10000
−10000≤n≤10000
−10000≤n≤10000
输入样例:
1000.00
1000.00
1000.00
输出样例:
10.000000
10.000000
10.000000
本题重难点和思路
本题本质上就是浮点数二分的经典应用:
假设要二分的左边界为l, 右边界为r,要开方的数为a
由于没有取整问题,所以就不存在整数二分的边界 l 和 r 更新时是否要+1的问题,更新边界时直接令 l = mid 或者 r = mid就可以了,时时刻刻保证答案在区间 [l, r] 内部,当 (r - l) 的值小于一个很小的数时, 就可以用 l 当作我们的答案
浮点数二分的难点:
- 二分左右边界的初始化问题,如果和整数二分一样,无脑初始化为:
int l = 0, r = a;
这样是正确的吗?
答案是不正确! 因为要二分的数很有可能是个小数,比如计算0.001的三次方根,我们都知道是0.1,如果和整数二分一样直接取要开方的数作为右边界,这个时候我们想要的最终结果0.1是不在设定的二分区间[0, 0.001]之间的,这个时候再去二分一定是有问题的。而且更何况奇数次方根的输入可能是负数,所有这些问题都要考虑到!
正确的初始化方法是我们二分边界l 和 r 的绝对值大小不能全部小于1,至少有一个绝对值大于1,这里由于有负数的存在,所以简便起见,可以这样初始化:
double l = min(-1.0, a), r = max(1.0, a);
或者也可以无脑写
double l = −10000, r = 10000;
-
惯性思维还把写 l = mid + 1或者 r = mid - 1;
前面说过由于没有取整问题,所以就不存在整数二分的边界 l 和 r 更新时是否要+1的问题,更新边界时直接令 l = mid 或者 r = mid就可以了 -
关于输出结果保留几位小数的问题:
printf默认保留6位小数,假如其他题目有其他特殊要求
可以使用下面方法指定保留位数:
比如我们想要保留9位小数:
printf("%.9lf",a); 保留小数点后9位
C++代码
#include<iostream>
#include<algorithm>
using namespace std;
int main(){
double a;
cin >> a; // scanf("%lf", &a);
double l = min(-1.0, a), r = max(1.0, a);
while(r - l > 1e-8) {
double mid = (l + r) / 2;
if(mid * mid * mid >= a) r = mid;
else l = mid;
}
printf("%lf", l); //printf默认保留6位小数
return 0;
}
总结
这道题是浮点数二分的经典问题,还是有很多细节需要注意的,这些问题只有实际写的时候才会发现,所以还是多动手写!
遇到n次方根问题(浮点数二分问题)只要按照上面的思路和代码模板,根据题目稍加改动就可以解决了!!!
欢迎大家关注本人公众号:编程复盘与思考随笔
(关注后可以免费获得本人在csdn发布的资源源码)