【二分】一元三次方程扩展
题目描述
众所周知,巨佬是不屑于做水题的,所以这么水的题只有交给你来完成啦。
有形如:ax3+bx2+cx+d=0 这样的一个一元三次方程。给出该方程中各项的系数(a,b,c,d 均为实数),并约定该方程存在三个不同实根(根的范围在-100至100之间),且根与根之差的绝对值>=1。要求由小到大依次在同一行输出这三个实根(根与根之间留有空格),并精确到小数点后2位。
输入输出格式
- 输入格式:
一行,4个实数A,B,C,D。 - 输出格式:
一行,三个实根,并精确到小数点后2位。
观察可发现本题的数据范围和精度其小无比,-100到100,精度小数点后两位,
就算是0.01 0.01的枚举也不过是O(20000)的复杂度,完全可以接受。
于是有此暴力代码。。
#include<stdio.h>
double a,b,c,d;
int main()
{
scanf("%lf %lf %lf %lf",&a,&b,&c,&d);
for(double i=-100.00;i<=100.00;i+=0.01)
{
if(a*i*i*i+b*i*i+c*i+d<0.00000001&&a*i*i*i+b*i*i+c*i+d>-0.00000001)
//注意判断浮点误差,一般认为答案差值不超过0.00000001为正确
printf("%.2f ",i);
}
return 0;
}
思考
此题极具特殊性,如果没有精度和范围的限制,又该如何求解
对于在线性顺序排序的结构中,我们往往使用二分查找。
但此题有一点不同·
如图为一元三次函数的图像
因为其不具有单调性,所以要将其分为三段依次求每段上的根。
列如此图可分为(-∞,-2)(2,1)(1,+∞)三段,再分别在每一段上二分查找。
那么问题也就转化为了如何寻找斜率为0的两个点的问题,对于此问题可以对原三次函数进行求导,求解一元二次的导函数得到分界点的值分别为x1,x2.
于是就可以分别为(-∞,x1)(x1,x2)(x2,+∞)三段上依次求解。
代码
#include<math.h>
#include<stdio.h>
double esp=1e-4;
int a,b,c,d;
int check(double x)
{
return a*x*x*x+b*x*x+c*x+d>0?1:0;
}
double find (double l,double r)
{
double temp=(l+r)/2;
while(r-l>=esp)
{
if(check(temp)+check(l)==0||check(temp)+check(l)==2)
{
l=temp;
}
else
r=temp;
temp=(l+r)/2;
}
return r;
}
int main()
{
scanf("%d %d %d %d",&a,&b,&c,&d);
double x1,x2,t;
double data=pow((4*b*b-12*a*c),0.5);
x1=(-2*b+data)/6/a;
x2=(-2*b-data)/6/a;//计算x1,x2。
if(x1>x2)
{
t=x1;
x1=x2;
x2=t;
}//判断x1,x2的大小关系,把小的放在x1.
double ans1=find(-100.0,x1);
double ans2=find(x1,x2);
double ans3=find(x2,100.0);
printf("%.2lf %.2lf %.2lf",ans1,ans2,ans3);
return 0;
}
后记
我还是太弱了,明明有公式还要硬算,算是被完爆了,
附一元三次方程求根公式