题目
有形如:这样的一个一元三次方程。给出该方程中各项的系数(a,b,c,d均为实数),并约定该方程存在三个不同实根(根的范围在−100至100之间),且根与根之差的绝对值≥1。要求由小到大依次在同一行输出这三个实根(根与根之间留有空格),并精确到小数点后2位。
提示:记方程,若存在2个数和,且,,则在(,) 之间一定有一个根。
输入输出格式
输入格式
一行,4个实数a,b,c,d。
输出格式
一行,3个实根,从小到大输出,并精确到小数点后2位。
输入输出样例
输入样例
1 -5 -4 20
输出样例
-2.00 2.00 5.00
解析
此题目考虑使用二分的方法求解。使用零点存在性定理:对连续函数f(x)若有f(a)*f(b)<0(a<b)则f(x)在区间(a,b)上至少存在一个解。这样就可以判断在一个区间中是否有解。
令条件为f(x)>=0,显然在上述区间(a,b)上条件具有单调性:在根的一侧f(x)都是负数,另一侧f(x)都是正数。题目中说明了任意两根之差不小于1,那么可以把[-100,100]等分成若干份[i,i+1)(这里左闭右开是为了防止端点处是零点导致得到重复解)。在每个小段中至多只有一个零点,这意味着这个区间上的条件具有单调性。
于是一个定义在实数区间上的二分方法就得出:如果中点的函数值和某端点的正负性相同,那么零点一定在中点的另一侧。
注意:实数之间不能直接比较是否相等,而是判断之间的差值是否小于eps。
同时二分的次数和精度有关,但是考虑每次二分的区间都可以减少一半,缩减的速度还是很快的,因此也是对数级别。与整数区间二分有一点微妙的区别,实数区间上的二分需要确定好精度。题目要求输出与保留两位小数,那么可以在二分端点相差不超过时停止二分来确保精度。
#include<iostream>
#include<cstdio>
#include<cmath>
#define eps 1e-4
using namespace std;
double a,b,c,d;
double f(double x){return a*pow(x,3)+b*pow(x,2)+c*x+d;//计算函数值
}
int main(){cin>>a>>b>>c>>d;for(int i=-100;i<=100;i++){double l=i,r=i+1,mid;if(fabs(f(l))<eps){//端点处处理,左闭右开printf("%.2lf ",l);}else if(fabs(f(r))<eps){continue;}else if(f(l)*f(r)<0){//在区间(l,r)上执行二分while(r-l>eps){mid=(l+r)/2;if(f(mid)*f(r)>0){//如果f(mid)和f(r)正负性相同,那么零点在mid左侧r=mid;}else{//否则在另一侧l=mid;}}printf("%.2lf ",l);}}
}