P1024 [NOIP2001 提高组] 一元三次方程求解
- 前言
- 题目
- 题目描述
- 输入格式
- 输出格式
- 样例 #1
- 样例输入 #1
- 样例输出 #1
- 题目分析
- 注意事项
- 代码
- 后话
- 额外测试用例
- 样例输入 #2
- 样例输出 #2
- 王婆卖瓜
- 题目来源
前言
没有前言,可能因为作者忘了编辑
题目
题目描述
有形如: 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
-2.00 2.00 5.00
题目分析
这题就算比较简单的一题,也有很多方法,有数学加成比较高的盛金法
和牛顿迭代法
等等,我就用比较简单易懂的暴力+二分来做。
当然有个前提是知道勘根定理,当然题目也有给。就是记方程
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<iostream>
#include<algorithm>
#include<cmath>
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;
}
后话
额外测试用例
因为忘记考虑浮点数精度而获得了一个用例
样例输入 #2
1 -4.65 2.25 1.4
样例输出 #2
-0.35 1.00 4.00
王婆卖瓜
感觉有收获或者想跟上我的进度刷题的,可以点个关注,或者点赞收藏评论都可以!
题目来源
NOIP 2001 提高组第一题
洛谷链接