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;
}