原题链接:P1024 [NOIP2001 提高组] 一元三次方程求解 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
# [NOIP2001 提高组] 一元三次方程求解
## 题目描述
有形如:$a x^3 + b x^2 + c x + d = 0$ 这样的一个一元三次方程。给出该方程中各项的系数($a,b,c,d$ 均为实数),并约定该方程存在三个不同实根(根的范围在 $-100$ 至 $100$ 之间),且根与根之差的绝对值 $\ge 1$。要求由小到大依次在同一行输出这三个实根(根与根之间留有空格),并精确到小数点后 $2$ 位。
提示:记方程 $f(x) = 0$,若存在 $2$ 个数 $x_1$ 和 $x_2$,且 $x_1 < x_2$,$f(x_1) \times f(x_2) < 0$,则在 $(x_1, x_2)$ 之间一定有一个根。
## 输入格式
一行,$4$ 个实数 $a, b, c, d$。
## 输出格式
一行,$3$ 个实根,从小到大输出,并精确到小数点后 $2$ 位。
## 样例 #1
### 样例输入 #1
```
1 -5 -4 20
```
### 样例输出 #1
```
-2.00 2.00 5.00
```
## 提示
**【题目来源】**
NOIP 2001 提高组第一题
思路:
可以用公式法,枚举法,二分等。此题解采用牛顿迭代法。既然题目确保一定有三个根,那么其导函数一定有两个极值点。三个根就分布在三个区间中。在三个区间中牛顿迭代即可。
AC代码:
#include<bits/stdc++.h>
using namespace std;
double a, b, c, d;
double x1, x2, x3,p,q;
double f(double x) { return a * x * x * x + b * x * x + c * x + d; }
double df(double x) { return 3 * a * x * x + 2 * b * x + c; }
//切线方程为y=f'(x0)(x-x0)+f(x0);
//令y=0 ——>x = x0 - f(x0) / f'(x0)
void bs(double l, double r)
{
double x=0, x0 = (l + r) / 2;//牛顿迭代法中,x0任意取,作为初始近似值,每次求出切线和此切线与x轴的交点,对此点(x,f(x))继续求切线,不断迭代。
while (fabs(x0 - x) > 1e-4)
x = x0 - f(x0) / df(x0), swap(x0, x);
//x0为上一次切线与X轴交点的横坐标
//x是由x0迭代求出的横坐标。每次swap,不断更新x0和x。不断迭代。
printf("%.2f ", x);
}
int main(){
cin >> a >> b >> c >> d;
p = (-b - sqrt(b * b - 3 * a * c)) / 3*a;//较小的极值点
q= (-b +sqrt(b * b - 3 * a * c)) / 3*a;//较大的极值点
bs(-100, p);
bs(p, q);
bs(q, 100);
return 0;
}