1000ms 128MB
题目描述
有形如:ax3+bx2+cx+d=0 这样的一个一元三次方程。给出该方程中各项的系数(a,b,c,d 均为实数),并约定该方程存在三个不同实根(根的范围在-100至100之间),且根与根之差的绝对值>=1。要求由小到大依次在同一行输出这三个实根(根与根之间留有空格),并精确到小数点后2位。
提示:记方程f(x)=0,若存在2个数x1和x2,且x1<x2,f(x1)*f(x2)<0,则在(x1,x2)之间一定有一个根。
输入输出格式
输入格式:
一行,4个实数A,B,C,D。
输出格式:
一行,三个实根,并精确到小数点后2位。
输入输出样例
输入样例
1 -5 -4 20
输出样例
-2.00 2.00 5.00
做这个题的时候我想的是二分,然后呢,由于学艺不精,二分没过,然后就想了一下暴力,果然暴力出奇迹,看了题解发现有用牛顿迭代法的,最后还有一个是用盛金公式做的,所以这个题就可以用四种方法做了。
暴力
#include <iostream> #include <stdio.h> #include <algorithm> #include <cmath> #include <math.h> #include <cstring> #include <string> #include <queue> #include <deque> #include <stack> #include <stdlib.h> #include <list> #include <map> #include <utility> #include <set> #include <bitset> #include <vector> #define pi acos(-1.0) #define inf 0x3f3f3f3f #define linf 0x3f3f3f3f3f3f3f3fLL #define ms(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; int min3(int a,int b,int c){return min(min(a,b),c);} int max3(int a,int b,int c){return max(max(a,b),c);} int gcd(int x, int y){if(y==0)return x;return gcd(y, x%y);} int main() { double a,b,c,d; scanf("%lf%lf%lf%lf",&a,&b,&c,&d); for(double i=-100;i<=100;i+=0.001) { double j=i+0.001; double y1=a*i*i*i+b*i*i+c*i+d; double y2=a*j*j*j+b*j*j+c*j+d; if(y1*y2<0) { double x=i; printf("%.2lf ",x); } } }
二分
因为区间很大,所以可以二分。
三个答案都在[ -100 , 100 ]范围内,两个根的差的绝对值>= 1 ,保证了每一个大小为 1 的区间里至多有 1 个解,也就是说当区间的两个端点的函数值异号时区间内一定有一个解,同号时一定没有解。那么我们可以枚举互相不重叠的每一个长度为 1 的区间,在区间内进行二分查找。
因为区间很大,所以可以二分。
三个答案都在[ -100 , 100 ]范围内,两个根的差的绝对值>= 1 ,保证了每一个大小为 1 的区间里至多有 1 个解,也就是说当区间的两个端点的函数值异号时区间内一定有一个解,同号时一定没有解。那么我们可以枚举互相不重叠的每一个长度为 1 的区间,在区间内进行二分查找。
#include <iostream> #include <stdio.h> #include <algorithm> #include <cmath> #include <math.h> #include <cstring> #include <string> #include <queue> #include <deque> #include <stack> #include <stdlib.h> #include <list> #include <map> #include <utility> #include <set> #include <bitset> #include <vector> #define pi acos(-1.0) #define inf 0x3f3f3f3f #define linf 0x3f3f3f3f3f3f3f3fLL #define ms(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; int min3(int a,int b,int c){return min(min(a,b),c);} int max3(int a,int b,int c){return max(max(a,b),c);} int gcd(int x, int y){if(y==0)return x;return gcd(y, x%y);} inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();return x*f;} double a,b,c,d; double fc(double x) { return a*x*x*x+b*x*x+c*x+d; } int main() { double l,r,m,y1,y2; int cnt=0; scanf("%lf%lf%lf%lf",&a,&b,&c,&d); for(int i=-100;i<=100;i++) { l=i; r=i+1; y1=fc(l); y2=fc(r); if(!y1) { printf("%.2lf ",l); cnt++; }//判断左端点,是零点直接输出。 //不能判断右端点,会重复。 if(y1*y2<0) { while(r-l>=0.001) { m=(l+r)/2; if(fc(m)*fc(r)<0) l=m; else r=m; } printf("%.2lf ",r); cnt++; } if(cnt==3) break;//找到三个就退出大概会省一点时间 } printf("\n"); return 0; }牛顿迭代法
对于一个已知的x值,每一次根据函数在这一点的导数,把x移动到,切线与x轴相交的地方。
即x[n+1]=x[n]-f(x)/f'(x),可以证明结果会趋近于函数的一个解,据说这种方法比二分要快。
不会,未完,待续
盛金公式
一元三次方程:aX的三次方+bX的二次方+cX+d=0
重根判别公式:
A=b的二次方-3ac
B=bc-9ad
C=c的二次方-3bd
重根判别公式:
A=b的二次方-3ac
B=bc-9ad
C=c的二次方-3bd
当A=B=0时,X1=X2=X3= -b/3a= -c/b = -3d/c
#include <iostream> #include <stdio.h> #include <algorithm> #include <cmath> #include <math.h> #include <cstring> #include <string> #include <queue> #include <deque> #include <stack> #include <stdlib.h> #include <list> #include <map> #include <utility> #include <set> #include <bitset> #include <vector> #define pi acos(-1.0) #define inf 0x3f3f3f3f #define linf 0x3f3f3f3f3f3f3f3fLL #define ms(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; int min3(int a,int b,int c) { return min(min(a,b),c); } int max3(int a,int b,int c) { return max(max(a,b),c); } int gcd(int x, int y) { if(y==0)return x; return gcd(y, x%y); } inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-')f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar(); return x*f; } int main() { double a,b,c,d; //定义四个系数 double x; double x1,x2,x3; //定义三个实根 cin>>a>>b>>c>>d; double A=b*b-3*a*c; //第一重根判别式 double B=b*c-9*a*d; //第二重根判别式 double C=c*c-3*b*d; //第三重根判别式 double del=B*B-4*A*C; //总判别式 if(A==B&&A==0) //如果A=0且B=0 { x1=x2=x3=(-1*b/(3*a)); //x1,x2,x3的值相同...... } else if(del==0) //如果△=0,x2与x3相同...... { x1=(-1*b/a)+B/A; x2=x3=-1*B/A/2; } else if(del<0) //如果△<0,有三实根...... { double T=(2*A*b-3*B*a)/(2*A*sqrt(A)); double _xt=acos(T); double xt=_xt/3; x1=(-1*b-2*sqrt(A)*cos(xt))/(3*a); x2=(-1*b+sqrt(A)*(cos(xt)+sqrt(3)*sin(xt)))/(3*a); x3=(-1*b+sqrt(A)*(cos(xt)-sqrt(3)*sin(xt)))/(3*a); }//由于题目说了只会出现实根,因此只有以上三种情况 if(x1>x2) { x=x1; x1=x2; x2=x; } if(x1>x3) { x=x3; x3=x1; x1=x; } if(x2>x3) { x=x3; x3=x2; x2=x; }//题目要求从小到大输出三实根,进行排序。 printf("%.2lf %.2lf %.2lf",x1,x2,x3); return 0; }