P1024 [NOIP2001 提高组] 一元三次方程求解 /1238:一元三次方程求解

P1024 [NOIP2001 提高组] 一元三次方程求解 /1238:一元三次方程求解

# [NOIP2001 提高组] 一元三次方程求解

## 题目描述

有形如:$a x^3 + b x^2 + c x + d = 0$  这样的一个一元三次方程。给出该方程中各项的系数($a,b,c,d$ 均为实数),并约定该方程存在三个不同实根(根的范围在 $-100$ 至 $100$ 之间),且根与根之差的绝对值 $\ge 1$。要求由小到大依次在同一行输出这三个实根(根与根之间留有空格),并精确到小数点后 $2$ 位。

提示:记方程 $f(x) = 0$,若存在 $2$ 个数 $x_1$ 和 $x_2$,且 $x_1 < x_2$,$f(x_1) \times f(x_2) < 0$,则在 $(x_1, x_2)$ 之间一定有一个根。

## 输入格式

一行,$4$ 个实数 $a, b, c, d$。

## 输出格式

一行,$3$ 个实根,从小到大输出,并精确到小数点后 $2$ 位。

## 样例 #1

### 样例输入 #1

```
1 -5 -4 20
```

### 样例输出 #1

```
-2.00 2.00 5.00
```

## 提示

**【题目来源】**

NOIP 2001 提高组第一题

 

1238:一元三次方程求解


时间限制: 1000 ms         内存限制: 65536 KB
提交数: 8791     通过数: 4561

【题目描述】

形如:ax3+bx2+cx+d=0ax3+bx2+cx+d=0 这样的一个一元三次方程。

给出该方程中各项的系数(a,b,c,da,b,c,d均为实数),并约定该方程存在三个不同实根(根的范围在−100−100至100100之间),且根与根之差的绝对值≥11。要求由小到大依次在同一行输出这三个实根(根与根之间留有空格),并精确到小数点后22位。

【输入】

一行,包含四个实数a,b,c,da,b,c,d,相邻两个数之间用单个空格隔开。

【输出】

一行,包含三个实数,为该方程的三个实根,按从小到大顺序排列,相邻两个数之间用单个空格隔开,精确到小数点后22位。

【输入样例】

1.0 -5.0 -4.0 20.0

【输出样例】

-2.00 2.00 5.00

【算法分析】

 这是一道有趣的解方程题。为了便于求解,设方程f(x)=ax^3+bx^2+cx+d=0,设根的值域(-100至100之间)中有x,其左右两边相距0.0005的地方有n和m两个数,即n=x-0.0005,m=x+0.0005。n和m间的距离(0.001)满足精度要求(精确到小数点后2位)。

有两种方法计算f(x)=0的根x:

(1)枚举法

根据根的值域和根与根之间的间距要求(>=1),我们不妨将根的值域扩大100倍(-10000<=x<=10000),依次枚举该区间的每一个整数值x,并在题目要求的精度内设定区间:n=\frac{x-0.05}{100},m=\frac{x+0.05}{100}。若区间端点的函数值f(n)和f(m)异号或者在区间端点n的函数值f(n)=0,则确定\frac{x}{100}为f(x)=0的一个根。

由此得出算法:

#include
#include
using namespace std;
double a,b,c,d,n,m;
double f(double x)//计算a*x^3+b*x^2+c*x+d 
{
	return a*x*x*x+b*x*x+c*x+d;
}//f函数 
signed main()
{
	scanf("%lf%lf%lf%lf",&a,&b,&c,&d);
	for(double i=-10000;i<=10000;i++)//枚举当前根*100的可能范围
	{
		n=(i-0.05)/100,m=(i+0.05)/100;//在题目要求的精度内设定区间 
		if(f(n)*f(m)<0 or f(n)==0)printf("%.2lf ",i/100);//若在区间两端的函数值异号或在n处的函数值为0,则确定i/100为根
	}
	return 0;
}
P1024 [NOIP2001 提高组] 一元三次方程求解 /1238:一元三次方程求解_第1张图片 给个关注吧 P1024 [NOIP2001 提高组] 一元三次方程求解 /1238:一元三次方程求解_第2张图片 给个关注吧

(2)分治法

枚举根的值域中的每一个整数x(-100<=x<=100)。由于根与根之差的绝对值>=1,因此设定搜索区间[l,r],其中l=x,r=x+1。

(1)若f(l)=0,则确定l为f(l)的根;

(2)若f(l)*f(r)>0,则确定根x不在区间[l,r]内,设定[r,r+1]为下一个搜索区间;

(3)若f(l)*f(r)<0,则确定根x在区间[l,r]内。

如果确定根在区间[l,r]内的话(f(l)*f(r)<0),如何在该区间找到根的确切位置。采用二分法,将区间[l,r]分成左右两个子区间:左子区间[l,x]和右子区间[x,r](其中x=\frac{l+r}{2}):

若f(l)*f(x)<=0,则确定根在左子区间[l,x]内,将x设为该区间的右指针(r=x),继续对左子区间进行对分;若f(l)*f(x)>0,则确定根在右子区间[x,r]内,将x设为该区间的左指针(l=x),继续对右子区间进行对分;

上述对分过程一直进行到区间的间距满足精度要求为止(r-l<0.001)。此时确定l为f(x)的根。

由此得出算法:

#include
#include
using namespace std;
double a,b,c,d,l,r,mid;
double f(double x)//将x代入函数 
{
	return a*x*x*x+b*x*x+c*x+d;
}
signed main()
{
	scanf("%lf%lf%lf%lf",&a,&b,&c,&d);
	for(double i=-100;i<=100;i++)//枚举每一个可能的根
	{
		l=i,r=i+1;//确定根的可能区间
		if(f(l)==0)printf("%.2lf ",l);//若l为根,则输出
		else if(f(l)*f(r)<0)//若根在区间[l,r]中
		{
			while(r-l>=0.001)//若区间[l,r]不满足精度要求,则循环
			{
				mid=(l+r)/2;//计算区间[l,r]的中间位置
				if(f(l)*f(mid)<1)r=mid;//若根在左子区间,则调整右指针
				else l=mid;//若根在右子区间,则调整左指针
			}
			printf("%.2lf ",l);//区间[l,r]满足精度要求,确定l为根
		}
	}
	return 0;
}
P1024 [NOIP2001 提高组] 一元三次方程求解 /1238:一元三次方程求解_第3张图片 给个关注吧

 

 

你可能感兴趣的:(html,前端,大数据,java,servlet)