• 洛谷P1024 [NOIP2001 提高组] 一元三次方程求解(优雅的暴力+二分,干净利落)


    前言

    没有前言,可能因为作者忘了编辑

    题目

    题目描述

    有形如: 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

    样例输出 #1

    -2.00 2.00 5.00
    
    • 1

    题目分析

      这题就算比较简单的一题,也有很多方法,有数学加成比较高的盛金法牛顿迭代法等等,我就用比较简单易懂的暴力+二分来做。
      当然有个前提是知道勘根定理,当然题目也有给。就是记方程 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) 之间一定有一个根。
      能使用我这个方法也有一个前提就是题目所说的“两个根之间距离大于等于1” 。所以我们可以先间隔为1遍历每个长度为1的区间,只需要200次就可以找到三个解的大致区间。
    然后就剩下三个分别为1 的区间是解,这是我们就可以用二分的方法利用勘根定理来做二分,只要l和mid的符号相同(相乘为正)说明不在这个区间内,我们就可以更换区间,知道l逼近于r,这时候就不需要考虑这个区间中有几个解。

    注意事项

    1.注意浮点数的处理,建议里面都使用浮点数,使用int可能会损失精度。
    2.浮点数关于相等的判断。使用fabs(x)<1e-6或者x<1e-6&&x>1e-6来判断x是否等于0,使用l-r<1e-6来判断l==r
    3.关于有解等于0的办法。我这里将符合的解都加上0.001,这样就不会出现等于0导致误判的情况了。

    代码

    轻松拿下,只有四个点。

    耶

    #include
    #include
    #include
    using namespace std;
    
    double a,b,c,d;
    double f(double x){//函数值
    	return a*x*x*x+b*x*x+c*x+d;
    }
    int negative(double x){//返回正负性或0
    	if(fabs(x)<=1e-6){
    		return 0;
    	}
    	else if(x<0){
    		return -1;		
    	}
    	else
    		return 1;
    }
    int main()
    {
    
    	cin>> a>>b>>c>>d;
    	double solution[100]={0};
    	int point =0;
    	double lastsol=negative(f(-100));
    	for(int i=-99;i<=100;i++){
    		if(f(i)*lastsol<=0||fabs(f(i))<1e-6)
    			solution[point++]=i+0.001;
    			lastsol=negative(f(i+0.001));
    	}
    	for(int i =0;i<3;i++){
    		double l=solution[i]-1,r=solution[i]+1;
    		while(r-l>0.001){
    			double mid = (l+r)/2;
    			if(f(l)*f(mid)>0)//说明l和mid在同侧,则解在mid和r之间
    				l=mid;
    			else
    				r=mid; 	
    		}
    		printf("%.2lf ",(l+r)/2); 
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    后话

    额外测试用例

    因为忘记考虑浮点数精度而获得了一个用例

    样例输入 #2

    1 -4.65 2.25 1.4
    
    • 1

    样例输出 #2

    -0.35 1.00 4.00
    
    • 1

    王婆卖瓜

    感觉有收获或者想跟上我的进度刷题的,可以点个关注,或者点赞收藏评论都可以!

    题目来源

    NOIP 2001 提高组第一题
    洛谷链接

  • 相关阅读:
    工程(十二)Ubuntu20.04LSD_SLAM运行
    IB数学、物理、生物、化学复习指导书分享
    Lua与C++交互
    权限管理框架Shiro renren-security权限管理结构
    数据结构初阶之顺序表、链表--C语言实现
    java spring cloud 工程企业管理软件-综合型项目管理软件-工程系统源码
    【学习笔记49】JavaScript的this指向
    猿创征文|中国移动 OneOS 万耦启物TB6612驱动电机
    如今传统企业如何做数字化转型?
    JFrame中有关于DefaultCloseOperation的使用及参数说明(含源码阅读)
  • 原文地址:https://blog.csdn.net/flyunicorninsky/article/details/134209811