P1024 [NOIP2001 提高组] 一元三次方程求解( 普及- ) 题解
题目传送门
思路:
暴力
可以直接暴力
二分搜索
二分搜索答案
盛金公式
盛金公式
一元三次方程:\(ax^3+cx^2+d=0\)
重根判别公式:
\(A=b^2-3ac\)
\(B=bc-9ad\)
\(C=c^2-3bd\)
当\(A=B=0\)时,\(X1=X2=X3= -b/3a= -c/b = -3d/c\)
牛顿迭代
牛顿迭代法:
对于一个已知的x值,每一次根据函数在这一点的导数,把x移动到,切线与x轴相交的地方。
即\(n+1]=x[n]-f(x)/f'(x)\)可以证明结果会趋近于函数的一个解。
勘根定理
勘根定理:
设函数f在闭区间\([a, b]\)中连续,且函数值\(f(a)\)与\(f(b)\)异号(即,一为正一为负)。则在区间\((a, b)\)中找到一个数c,使得\(f(c) = 0\)(即,\(c\)为函数\(f\)的根)。
Code:
暴力
#include <bits/stdc++.h>
using namespace std;
int main()
{
double a,b,c,d;
cin>>a>>b>>c>>d;
for(double i=-100;i<=100;i+=0.001)//因为根的范围在-100~100之间,直接枚举-100~100的所有解
{
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>=0 and y2<=0||y1<=0 and y2>=0)
{
double x=(i+j)/2;
printf("%.2lf ",x);
}
}
}
二分搜索
#include<bits/stdc++.h>
using namespace std;
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,x1,x2;
int s=0,i;
cin>>a>>b>>c>>d;
for (i=-100;i<100;i++)
{
l=i;
r=i+1;
x1=fc(l);
x2=fc(r);
if(!x1)//判断左端点,是零点直接输出。
{
printf("%.2lf ",l);
s++;
}
//不能判断右端点,会重复。
if(x1*x2<0)//区间内有根。
{
while(r-l>=0.001)//二分控制精度。
{
m=(l+r)/2; //middle
if(fc(m)*fc(r)<=0)
l=m;
else
r=m;//计算中点处函数值缩小区间。
}
printf("%.2lf ",r);//输出右端点。
s++;
}
if (s==3)
break;//找到三个就退出大概会省一点时间
}
return 0;
}
盛金公式
#include <bits/stdc++.h>
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;
}
牛顿迭代法
#include <bits/stdc++.h>//不要问为什么我这个蒟蒻写的出来,问就是抄的
using namespace std;
struct func3 {
double a, b, c, d;
func3(double A = 0, double B = 0, double C = 0, double D = 0) {
a = A;
b = B;
c = C;
d = D;
}
double operator()(double x) {
return ((a * x + b) * x + c) * x + d;
}
double dvt(double x) {
return (3.0 * a * x + 2.0 * b) * x + c;
}
};
void func3solve(func3 f, double st, double& val, double& sol) {
for (int i = 1; !(abs(f(st)) < 1e-6) && i <= 100; i++) {
st = st - f(st) / f.dvt(st);
}
val = f(st);
sol = st;
}
double fix2(double sol) {
return (double)int(sol * 100.0 + (sol > 0 ? 0.5 : -0.5)) / 100.0;
}
set<double>solutions;
int main() {
double a, b, c, d;
scanf("%lf%lf%lf%lf", &a, &b, &c, &d);
func3 f(a, b, c, d);
for (double i = -100.0; i <= 100.0; i += 0.5) {
double val, sol;
func3solve(f, i, val, sol);
sol = fix2(sol);
if (abs(val) < 1e-6 && solutions.find(sol) == solutions.end())
solutions.insert(sol);
}
for (set<double>::iterator it = solutions.begin(); it != solutions.end(); it++) {
double x = (*it);
printf("%.2lf ", x);
}
return 0;
}
勘根定理
#include <bits/stdc++.h>
using namespace std;
double a,b,c,d;
double f(double x)
{
return (x*x*x*a+x*x*b+x*c+d);//计算一元三次方程的值
}
int main()
{
double x1,x2,xx;
scanf("%lf%lf%lf%lf",&a,&b,&c,&d);//都是实数,不一定非是整数
for(double i=-100;i<=100;i++) {//分别枚举
x1=i;x2=i+1;
if(f(x1)==0)//说明为零,直接输出
printf("%.2lf ",i);
if(f(x1)*f(x2)<0) {//小于零说明有零点,即说明有解。
while(x2-x1>=0.001) {//范围
xx=(x1+x2)/2;//二分
if(f(x1)*f(xx)<=0)//判断零点在x1到xx还是xx到x2
x2=xx;//更新
else
x1=xx;
}
printf("%.2lf ",x1);//输出
}
}
}