当前位置: 首页 > news >正文

【二分搜索 C/C++】洛谷P1024 一元三次方程求解

2025 - 02 - 13 - 第 52 篇
作者(Author): 郑龙浩 / 仟濹(CSND)
【二分搜索】

P1024 一元三次方程求解

题目描述

有形如: a x 3 + b x 2 + c x + d = 0 a x^3 + b x^2 + c x + d = 0 ax3+bx2+cx+d=0 这样的一个一元三次方程。给出该方程中各项的系数( a , b , c , d a,b,c,d a,b,c,d 均为实数),并约定该方程存在三个不同实根(根的范围在 − 100 -100 100 100 100 100 之间),且根与根之差的绝对值 ≥ 1 \ge 1 1。要求由小到大依次在同一行输出这三个实根(根与根之间留有空格),并精确到小数点后 2 2 2 位。

提示:记方程 f ( x ) = 0 f(x) = 0 f(x)=0,若存在 2 2 2 个数 x 1 x_1 x1 x 2 x_2 x2,且 x 1 < x 2 x_1 < x_2 x1<x2 f ( x 1 ) × f ( x 2 ) < 0 f(x_1) \times f(x_2) < 0 f(x1)×f(x2)<0,则在 ( x 1 , x 2 ) (x_1, x_2) (x1,x2) 之间一定有一个根。

输入格式

一行, 4 4 4 个实数 a , b , c , d a, b, c, d a,b,c,d

输出格式

一行, 3 3 3 个实根,从小到大输出,并精确到小数点后 2 2 2 位。

输入输出样例 #1

输入 #1

1 -5 -4 20

输出 #1

-2.00 2.00 5.00

说明/提示

【题目来源】

NOIP 2001 提高组第一题

思路

首先要分析题目:
判断是否为 “根” 只有两种方法:

**前提:x 是以 1 个单位长度依次递增,而不是2 3 4什么的,就是一个单位长度 **

  • f( x ) == 0x 百分百为 “根”

  • left < right 且 f( left ) * f( right ) < 0,在 left 与 right 之间会有一个 x,这个 x 就是根

    为什么要确定 leftright 呢??

    因为 left 与 right 是两个整数,而这两个整数的距离实际上是1,比如 1与2,2与3,然后再确定具体的值1.23, 2.45(随便举例)等

    并且 f( left ) f( right ),一个为 正 一个为负数,中间可定有一个 f( x ) 为 0

    所以确定下来 left, right之后的任务就是:将 x 求出来,如何求???

    使用二分算法

    • 范围为 left ~ right

    • 不断 left ~ right / 2 ,直到满足 left < right right - left <= 0.001

      right - left <= 0.001 意思就是: 确定下来这两个数之间的差为三个小数,这样就能将 将结果变为 left 了

      Eg: 最后一层循环:

      left = 1.234
      right = 1.235
      ans = 1.234;
      // 四舍五入: 1.23
      

      当二分法的区间长度缩小到0.001时,取中间值作为近似解,四舍五入到两位小数时误差不会超过0.005。例如,假设真实解在某个区间[left, right],其中right - left = 0.001。此时中间值mid的误差最大是0.0005,这样在保留两位小数时,结果会是正确的。例如,若真实解是1.2345,而mid是1.234或1.235,四舍五入到两位小数后都是1.23或1.24,但需要确保在两位小数时的正确性。或者,可能作者认为当区间足够小时,可以直接输出区间端点中的某个值,作为近似解。

代码流程

  1. 输入系数a, b, c, d
  2. 遍历i-100到100,每次检查区间[i, i+1]是否存在根。
  3. 对于每个i,先检查f(i)是否为0,如果是就直接输出。
  4. 如果f(i)*f(i+1) < 0,说明该区间有一个根,调用二分法查找。
  5. 二分法在区间内不断缩小区间,直到区间长度小于0.001,然后返回左端点作为近似解。
  6. 所有找到的根按顺序输出。

代码

// 洛谷P1024一元三次方程求解
// 2025-02-12
// 郑龙浩 / 仟濹
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
double a, b, c, d;
// 查找 left 与 right 之间的 x
double binary_search_self( double left, double right );
// 计算带入方程结果
double fun( double x );
int main( void ){

    cin >> a >> b >> c >> d;
    for( int i = -100; i <= 99; i ++ ){
        // if fun( i ) 的值 == 0.,就代表 i 就是根
        // 因为 i 是从小到大判断的,无需sort
        if ( fun( i ) == 0 ){
            // 记住: i 要转换成 double 类型,不然 直接 直接用 %f 打印 int ,结果是0
            // 因为我找了半天错,没找出来,就是这里写错了,要强制类型转换(小细节)
            printf( "%.2f ", (double)i );
        }
        if ( fun( i ) * fun( i + 1 ) < 0 ){ // 确定下来这个根在 i 与 i + 1 间
            double ans = binary_search_self( i, i + 1 );
            printf( "%.2f ", ans );
        }
    }
    return 0;
}
// 查找 left 与 right 之间的 x
double binary_search_self( double left, double right ){
    double middle;
    while( right - left > 0.001 ){
        middle = ( left + right ) / 2.0;
        if( fun( left ) * fun( middle ) < 0 ) 
            right = middle;//说明根在left~mid中 
		else 
            left = middle;//说明根在mid~right中 
    }
    return ( left + right ) / 2;// 精度更高,可以不left - AI 给我提的意见
    // 第一次写的做法
    // return left; // 输入left 千万不要right 比如: 1.234,1.235,输出第二个四舍五入的1.24
}
// 计算带入方程结果
double fun( double x ){
    double ans = a * pow(x, 3) + b * pow(x, 2) + c * x + d;
    return ans;
}

相关文章:

  • 【deepseek 部署中的常见问题及解决方案--亲测有效】
  • 机器视觉中的3d和2d的区别
  • Unity实现UI拖拽
  • 《深度LSTM vs 普通LSTM:训练与效果的深度剖析》
  • 在 UniApp 中封装并连接 WebSocket 及重连机制实现
  • AcWing 798. 差分矩阵
  • 华为小艺支持DeepSeek
  • Python教学-最常用的标准库之一——OS库
  • golang基础库
  • 【SpringBoot苍穹外卖】debugDay03.5
  • Java 设计模式之备忘录模式
  • 2.11学习
  • Unity 命令行设置运行在指定的显卡上
  • Hyper-V管理器连接服务器提示你没有完成此任务所需的权限
  • 日常故障排查 - Linux常用命令
  • C++ 中的继承与派生
  • Windchill 成套的解决方案
  • Linux Mem -- ARM8.5-A Memory Tagging Extension
  • 人工智能大模型技术剖析:分类、对比与性能洞察
  • 【情感识别】SECap: Speech Emotion Captioning with Large Language Model 论文阅读
  • 政策一视同仁引导绿色转型,企业战略回应整齐划一?
  • 武汉旅游体育集团有限公司原党委书记、董事长董志向被查
  • 王毅同巴基斯坦副总理兼外长达尔通电话
  • 河北邯郸一酒店婚宴发生火灾:众人惊险逃生,酒店未买保险
  • 习近平会见缅甸领导人敏昂莱
  • 19个剧团15台演出,上海民营院团尝试文旅融合新探索