一元三次方程求解(数学、二分)
https://www.luogu.com.cn/problem/P1024
Description
有形如:ax3+bx2+cx+d=0 这样的一个一元三次方程。
给出该方程中各项的系数(a,b,c,d 均为实数),并约定该方程存在三个不同实根(根的范围在-100至100之间),且根与根之差的绝对值>=1。要求三个实根。
Input
四个实数:a,b,c,d
Output
由小到大依次在同一行输出这三个实根(根与根之间留有空格),并精确到小数点后2位
Sample Input
1 -5 -4 20
Sample Output
-2.00 2.00 5.00
HINT
数据规模和约定
|a|,|b|,|c|,|d|<=10
记方程f(x)=0,若存在2个数x1?和x2?,且x1?<x2?,f(x1?)×f(x2?)<0,则在(x1?,x2?)之间一定有一个根。
这题解法比较多
因为区间很大,所以可以二分。
题目保证三个答案都在[-100,100]范围内,且两个根的差的绝对值>=1,所以每一个大小为1的区间里至多有1个解。
当区间的两个端点的函数值异号时区间内一定有一个解,这个时候我们可以在区间内进行二分查找ans,如果同号则该区间一定没有解。
刚开始遍历i从-10000到10000了,然后i再除以100当成x的值,没想到这样显示TLE了。。。
#include <stdio.h>
#include <iostream>
#include <string>
#include <string.h>
#include <algorithm>
#include <map>
#include <vector>
#include <set>
#include <stack>
#include <queue>
#include <math.h>
#include <sstream>
using namespace std;
typedef long long LL;
const int INF=0x3f3f3f3f;
const int maxn=1e5+10;
const double eps = 1e-8;
const double PI = acos(-1);
double a,b,c,d;//开成double吧,免得出错
vector<double> ans;
double f(double x)
{
return a*x*x*x+b*x*x+c*x+d;
}
double solve(double x1,double x2) //在x1和x2中二分找答案
{
double L=x1,R=x2;
while(R-L>1e-4) //这里的eps一般要比题中要求的精度(1e-2)多两位。
{
double mid=(L+R)/2;
double t1=f(L);
double t2=f(mid);
double t3=f(R);
if(t2==0) return mid;
if(t1*t2<0) R=mid;
else if(t2*t3<0) L=mid;
}
return (L+R)/2;//返回L,R,(L+R)/2都行
}
int main()
{
#ifdef DEBUG
freopen("sample.txt","r",stdin);
#endif
scanf("%lf %lf %lf %lf",&a,&b,&c,&d);
double pre=f(-100.0);
for(int i=-100;i<=100;i++)
{
double t=f(i);
if(t!=0)
{
if(t*pre<0) ans.push_back(solve(i-1,i));
}
else ans.push_back(i);
pre=t;
}
for(int i=0;i<3;i++)
printf(i==2?"%.2f\n":"%.2f ",ans[i]);
return 0;
}下面是我看别的大佬写的
盛金公式:



#include <iostream>
#include <math.h>
#include <iomanip>
using namespace std;
int main()
{
double a,b,c,d;
double as,bs,t,si;
double x1,x2,x3;
cin>>a>>b>>c>>d;
as=b*b-3*a*c;
bs=b*c-9*a*d;
t=(2*as*b-3*a*bs)/(2*sqrt(as*as*as));
si=acos(t);
x1=(-b-2*sqrt(as)*cos(si/3))/(3*a);
x2=(-b+sqrt(as)*(cos(si/3)+sqrt(3)*sin(si/3)))/(3*a);
x3=(-b+sqrt(as)*(cos(si/3)-sqrt(3)*sin(si/3)))/(3*a);
cout<<fixed<<setprecision(2)<<x1<<" ";
cout<<fixed<<setprecision(2)<<x3<<" ";
cout<<fixed<<setprecision(2)<<x2<<" ";
return 0;
}最后是EarthGiao大佬写的题解:
导数+勘根定理+牛顿迭代.
先对函数求导,f‘(x)=3ax^2+2*bx+c.
然后直接求根公式求f‘(x)=0的点,也就是函数极点.
这题保证有三个不定根,所以有两个单峰.
我们分别设这两个点为p,q.
然后显然的必有三个根在[-100,p),[p,q],(q,100]三个区间内 (两极点间必定存在零点,勘根定理).
然后用神奇的牛顿迭代法多次迭代就好了.
#include<iostream>
#include<cstdio>
#include<cmath>
#define eps 1e-4
using namespace std;
double x1,x2,x3,a,b,c,d;
double f(double x){return a*x*x*x+b*x*x+c*x+d;}
double df(double x){return 3*a*x*x+2*b*x+c;}
double slove(double l,double r)
{
double x,x0=(l+r)/2;
while(abs(x0-x)>eps)
x=x0-f(x0)/df(x0),swap(x0,x);
return x;
}
int main()
{
cin>>a>>b>>c>>d;
double p=(-b-sqrt(b*b-3*a*c))/(3*a);
double q=(-b+sqrt(b*b-3*a*c))/(3*a);
x1=slove(-100,p),x2=slove(p,q),x3=slove(q,100);
printf("%.2lf %.2lf %.2lf",x1,x2,x3);
return 0;
}-
相关推荐
xceman 2020-10-13
算法与数学之美 2020-10-07
Anscor 2020-10-05
liwg0 2020-09-08
数学爱好者 2020-08-31
thermodynamicB 2020-08-11
夕加加 2020-07-20
willowwgx 2020-07-18
kuoying 2020-07-16
Anscor 2020-07-14
starletkiss 2020-07-08
kingzone 2020-06-27
xceman 2020-06-27
算法与数学之美 2020-06-21
kuoying 2020-06-21
秒懂数学 2020-06-17