P1024 [NOIP 2001 提高组] 一元三次方程求解
题目描述
有形如:\(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
说明/提示
【题目来源】
NOIP 2001 提高组第一题
Tip
解题思路
-
定义函数: (\(f(x) = ax^3 + bx^2 + cx + d\))
-
已知:
-
有 3 个不同实根
- 根在 ([-100, 100])
- 任意两根间距 ≥ 1
👉 可以按整数区间枚举: 遍历区间 ([i, i+1])(i 从 -100 到 99)
-
若满足:
-
f(i) == 0→ i 是一个根 f(i) * f(i+1) < 0→ 区间内有根(介值定理)
👉 对每个有根的区间,用二分法求精确值
二分细节
在区间 [l, r] 内:
- 不断取
mid -
判断
f(l) * f(mid): -
< 0 → 根在左边
- 否则 → 根在右边
迭代到精度(如 0.001)即可
C++代码
#include <bits/stdc++.h>
using namespace std;
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;
if (fabs(f(l)) < 1e-8) {
printf("%.2f ", l);
continue;
}
if (f(l) * f(r) < 0) {
// 二分
while (r - l > 1e-4) {
double mid = (l + r) / 2;
if (f(l) * f(mid) <= 0)
r = mid;
else
l = mid;
}
printf("%.2f ", l);
}
}
return 0;
}
核心总结
- 枚举区间找“变号”
- 每个区间用二分逼近
- 利用:连续函数 + 介值定理