题目来源:
1、[NOIP2001 提高组] 一元三次方程求解 - 洛谷
2、《深入浅出程序设计竞赛--基础篇》------汪楚奇P183
解题思路:
1、暴力求解
先不说三次方程的求解方法,本题直接用暴力求解也是可以得到答案的。我们可以直接遍历[-100, 100] 范围内的每个可能的浮点数值,并检查哪些值使得方程的结果接近于零。由于题目要求根与根之间的差的绝对值至少为 1,所以我们可以选一个较小的步长(例如,0.01)在这个范围内进行遍历,每次计算方程在该点的值,如果值接近于零,则认为找到了一个根。22、
#include <iostream>
#include <cmath>
#include <iomanip>
using namespace std;
#define EPS 0.01 //设置步长
double f(double a, double b, double c, double d, double x) {
return a * x * x * x + b * x * x + c * x + d;
}
int main() {
double a, b, c, d;
cin >> a >> b >> c >> d;
for (double i = -100; i <= 100; i += EPS) {
if (fabs(f(a, b, c, d, i)) < EPS) {
cout << fixed << setprecision(2) << i << " ";
i += 1.0 - EPS; // 跳过与这个根相近的一些值,避免找到重复值并跳过绝对值小于1的值
}
}
return 0;
}
2、用二分查找
使用二分查找是为了降低时间复杂度,其实也就是遍历根在[-100,100]之间用二分搜索找值。
步骤如下:
1、对于 -100 到 100 之间的每个整数 i
,检查区间 [i, i+1]
是否包含一个根。我们可以通过检查 f(i)
和 f(i+1)
的符号是否不同来确定这一点。
2、如果 [i, i+1]
区间内存在根,我们可以在这个区间内进行更精细的搜索(例如,使用二分搜索)以找到根的近似位置。
书上的示例代码如下:
#include <cstdio>
#include <iostream>
#include <cmath>
using namespace std;
#define eps 1e-4
double A, B, C, D;
double f(double x) {
return A * x * x * x + B * x * x + C * x + D;
}
int main() {
cin >> A >> B >> C >> D;
for (int i = -100; i <= 100; i++) {
double L = i, R = i + 1, mid; //这里只处理区间[L,R)上的根
if (fabs(f(L)) < eps) {//如果L是根,可以直接输出
printf("%.2lf ", L);
}
else if (fabs(f(R)) < eps) {//如果R是根,跳过
continue; // 如果 R 是根,则在下一次迭代中处理
}
else if (f(L) * f(R) < 0) {//在(L,R)上有根,执行二分
while (R - L > eps) {
mid = (L + R) / 2;
if (f(mid) * f(R) > 0) {
R= mid; //如果f(mid)和f(R)正负性相同,那么零点在mid左侧
}
else {
L = mid;//否则在另一侧
}
}
printf("%.2lf ", L);
}
}
return 0;
}