一元三次方程求解

GitHub跳转原题关系图返回列表

利用根是两位小数且两根之差至少为 1 的约定,在 [-100,100] 上以 0.01 步长从左到右扫描,命中 |f(x)|≤1e-5 的网格点即根,三次命中天然有序。

OJ: roj

题目 ID: 1238

难度:入门

标签:枚举数学python

创建: 2026-09-30 01:09

更新: 2026-10-05 06:24

题目描述

给定实系数 a,b,c,da,b,c,d,求解方程 ax3+bx2+cx+d=0ax^3+bx^2+cx+d=0。约定存在三个不同实根,根在 [−100,100][-100,100] 内且任意两根之差的绝对值 ⩾1\geqslant 1。输入一行四个实数,输出一行三个实根,按从小到大顺序,精确到小数点后 22 位。

思路

根都是两位小数,因此在 [−100,100][-100,100] 上以 0.010.01 为步长从左到右扫描,用秦九韶形式计算 f(x)f(x),遇到 ∣f(x)∣⩽10−5|f(x)|\leqslant 10^{-5} 即记录为根并继续向右找下一个。根间距 ⩾1\geqslant 1 保证不会漏或重。

参考代码

Python 版:

python
#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn  https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-09-30 01:12
# update_at: 2026-09-30 01:12

import sys

EPS = 1e-5  # 判根阈值:网格点处 |f(x)| ≤ 1e-5 即认为是根(与评测参考实现一致)


def solve() -> None:
    a, b, c, d = map(float, sys.stdin.buffer.read().split())

    def f(x: float) -> float:
        """方程左端 ax^3+bx^2+cx+d 在 x 处的取值(秦九韶形式)。"""
        return ((a * x + b) * x + c) * x + d

    roots: list[float] = []
    x = -100.0
    for _ in range(3):  # 依次找最小、次小、最大的根
        while x - 100 <= EPS:  # 网格从 -100 扫到 100(含端点,与参考实现一致)
            if abs(f(x)) <= EPS:
                break  # 命中一个根:根是两位小数且两两之差 ≥ 1,步长 0.01 必能扫到
            x += 0.01
        roots.append(x)  # 命中的网格点;若数据不满足"三个实根"约定则与参考实现同样扫到界外
        x += 0.01  # 跳过当前根所在的网格点,继续找下一个根

    print(' '.join(f'{r:.2f}' for r in roots))


if __name__ == "__main__":
    solve()

C++ 版(同一算法):

cpp
/**
 * Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
 * rbook: -> https://rbook.roj.ac.cn  https://rbook2.roj.ac.cn
 * rainboy的学习导航网站: https://idx.roj.ac.cn
 * create_at: 2026-10-05 06:19
 * update_at: 2026-10-05 06:24
 */

#include <iostream>
#include <iomanip>
#include <cmath>
using namespace std;

typedef long long ll;

const double EPS = 1e-5; // 判根阈值:|f(x)| ≤ 1e-5 即认为是根

double a, b, c, d; // 一元三次方程 ax^3 + bx^2 + cx + d = 0 的系数

// 秦九韶形式求多项式在 x 处的取值
inline double f(double x) {
    return ((a * x + b) * x + c) * x + d;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    cin >> a >> b >> c >> d;

    double x = -100.0; // 从区间左端点开始,以 0.01 为步长向右扫描
    for (ll i = 0; i < 3; ++i) { // 依次找出三个根
        while (x - 100.0 <= EPS) {
            if (fabs(f(x)) <= EPS) {
                break; // 命中一个网格点根
            }
            x += 0.01;
        }
        double root = round(x * 100.0) / 100.0; // 把浮点误差拉回两位小数网格点
        if (i > 0) {
            cout << ' ';
        }
        cout << fixed << setprecision(2) << root;
        x += 0.01; // 跳过当前根所在网格点,继续寻找下一个根
    }
    cout << '\n';

    return 0;
}