时间限制: 1 Sec 内存限制: 128 MB
[提交] [状态]
题目描述
有形如:ax3+bx2+cx+d=0 这样的一个一元三次方程。给出该方程中各项的系数(a,b,c,d 均为实数),并约定该方程存在三个不同实根(根的范围在-100至100之间),且根与根之差的绝对值>=1。要求三个实根。
输入
四个实数:a,b,c,d
输出
由小到大依次在同一行输出这三个实根(根与根之间留有空格),并精确到小数点后2位
样例输入 Copy
1 -5 -4 20
样例输出 Copy
-2.00 2.00 5.00
提示
数据规模和约定
|a|,|b|,|c|,|d|<=10
题解:
由题意知方程一定有3个不同的实根,根据数学知识,对应的三次函数的图像一定是增-减-增或者减-增-减两种可能。不妨讨论增-减-增的情况:易知图像在每个单调区间内都有一个实数根,可以在每个单调区间上利用二分法求解对应的一个实数根(这里体现了分治的思想),而单调区间的分界点就是2个极值点,可以通过求导、解一元二次方程、代入求根公式求得。减-增-减对应的情况类似。
需要注意的几个细节:
1.需要对a的正负进行讨论,a>0时图像增-减-增,a<0时图像减-增-减。
2.a的正负对两个极值点的横坐标的大小关系有影响(会影响到二分时的左右端点的确定,具体可比较代码),可以通过代入具体的值+printf()语句来判断。
3.第一个区间的左端点值和最后一个区间的右端点值不是无穷,根据题意可以分别取成-200和200。
4.高一数学学过二分法求方程的近似解,根据区间中点横坐标对应的函数值与0的关系来决定取哪一部分区间(二分法的基本思想),同时还要注意区间的增减性对选择区间的影响,代码中对其作出了讨论。
5.实数的二分法,一是注意左右端点的移动,直接令l=mid
或r=mid
,最后输出l,r其中之一,二是注意循环进行的条件:l+eps<r
,这里的eps一般要比题中要求的精度(1e-2)多两位。
6.可以利用matlab求解一元三次方程,根据matlab求解的结果验证程序的正确性。首先建立m文件,然后在m文件中输入roots([a b c d])
,点击运行后命令行窗口输出对应的3个解,这里的a,b,c,d可以为小数,也可以为整数,数字之间有空格,输入的4个数一定要满足有3个符合题意的实数解。
输入:roots([-1.5 4.2 8.9 -3.4])
命令行运行结果:
ans = 4.1096 -1.6449 0.3353
程序运行结果:
-1.5 4.2 8.9 -3.4
-1.64 0.34 4.11
Process returned 0 (0x0) execution time : 1.825 s
Press any key to continue.
AC代码:
#include<stdio.h>
#include<math.h>
#define eps 1e-4
const double INF_MIN=-200;
const double INF_MAX=200;
int main()
{
double a;
double b;
double c;
double d;
double x_01;
double x_02;
double l;
double r;
double mid;
scanf("%lf %lf %lf %lf",&a,&b,&c,&d);
x_01=(-b-sqrt(b*b-3*a*c))/(3*a);
x_02=(-b+sqrt(b*b-3*a*c))/(3*a);
//printf("x_01=%.2f,x_02=%.2f\n",x_01,x_02);用于判断2个极值点横坐标的大小关系
if(a>0)
{
l=INF_MIN;
r=x_01;
while(l+eps<r)//循环条件
{
mid=(l+r)/2;
if(a*mid*mid*mid+b*mid*mid+c*mid+d<0)l=mid;
else r=mid;
}
printf("%.2f ",l);
l=x_01;
r=x_02;
while(l+eps<r)
{
mid=(l+r)/2;
if(a*mid*mid*mid+b*mid*mid+c*mid+d<0)r=mid;
else l=mid;
}
printf("%.2f ",l);
l=x_02;
r=INF_MAX;
while(l+eps<r)
{
mid=(l+r)/2;
if(a*mid*mid*mid+b*mid*mid+c*mid+d<0)l=mid;
else r=mid;
}
printf("%.2f",l);
}
else if(a<0)
{
l=INF_MIN;
r=x_02;
while(l+eps<r)
{
mid=(l+r)/2;
if(a*mid*mid*mid+b*mid*mid+c*mid+d<0)r=mid;
else l=mid;
}
printf("%.2f ",l);
l=x_02;
r=x_01;
while(l+eps<r)
{
mid=(l+r)/2;
if(a*mid*mid*mid+b*mid*mid+c*mid+d<0)l=mid;
else r=mid;
}
printf("%.2f ",l);
l=x_01;
r=INF_MAX;
while(l+eps<r)
{
mid=(l+r)/2;
if(a*mid*mid*mid+b*mid*mid+c*mid+d<0)r=mid;
else l=mid;
}
printf("%.2f",l);
}
return 0;
}