题目及翻译
题面
有形如:ax^3 + bx^2 + cx + d
这样的一个一元三次方程。给出该方程中各项的系数(a,b,c,da,b,c,d 均为实数),并约定该方程存在三个不同实根(根的范围在 -100−100 至 100100 之间),且根与根之差的绝对值≥1。要求由小到大依次在同一行输出这三个实根(根与根之间留有空格),并精确到小数点后 22 位。
提示:记方程 f(x) = 0,若存在2个数x1和x2,且x1<x2,f(x1) * f(x2) < 0,则在(x1,x2)之间一定有一个跟。
输入
一行,4个实数a,b,c,d。
输出
一行,3个实根,从小到大输出,并精确到小数点后2位
输入样例
1 -5 -4 20
输出样例
-2.00 2.00 5.00
题目思路
1.显然是一题二分答案的题目,涉及高精度,那么就要考虑二分的误差,我这里取了1e-15(一般我取1e20但是这次超时了)
2.题目给定范围内一定存在三个方程的解且间隔≥1,分析样例的函数图像可知,函数在给定范围之间一定存在两个极值点,且一个是极大值一个是极小值。将函数求导并代入求根公式,就能得出两个极值点的位置,然后再在由两个极值点将原范围分割出的三个部分中分别搜出一个解,题目保证了一定存在解所以中途根本不用判断解存在的问题,省去了很多麻烦
注意事项
函数值的过程不寄存会超时,数据范围l和r要用函数值去约束
AC代码
C/C++(代码几乎没有变更)
用时23MS 内存624K 长度1020B
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double lf;
lf t,n,m,k,a,b,c,d,u,v,w,space,blank,flag,cnt,cur,sum,res;
void init() {
return;
}
inline lf check(lf x) {
return a * x * x * x + b * x * x + c * x + d;
}
lf search(lf l,lf r) {
lf mid,mc,lc,rc;
lc = check(l);
rc = check(r);
if(lc > rc){//如果两者函数值与坐标成反相关,则要翻转左右令l到r的函数段递增
swap(lc,rc);
swap(l,r);
}
while(lc < rc && fabs(l - r) > 1e-15) {//考虑精度误差,否则会死循环
mid = (l + r) / 2;
mc = check(mid);
if(mc < 0)l = mid,lc = mc;
else if(mc > 0)r = mid,rc = mc;
else return mid;
}
return l;
}
void preProgram() {
return;
}
void solve() {
init();
cin>>a>>b>>c>>d;
lf m1 = (-2 * b - sqrt(4 * b * b - 12 * a * c)) / (6 * a);
lf m2 = (-2 * b + sqrt(4 * b * b - 12 * a * c)) / (6 * a);
lf r1 = search(-100, m1);
lf r2 = search( m1, m2);
lf r3 = search( m2, 100);
cout<<fixed<<setprecision(2)<<r1<<" "<<r2<<" "<<r3<<endl;
return;
}
int main() {
preProgram();
solve();
return 0;
}
本文作者 CSDN@扶她小藜
个人主页链接 https://blog.csdn.net/weixin_44579869