一 题目与示例
二 题解
1.暴力求解骗分法:D
#include <iostream>
#include <cmath>
#include <iomanip>
using namespace std;
int main()
{
double a,b,c,d;
int cnt = 0;
cin >> a >> b >> c >> d;
for(double i = -100; i <=100.00; i+=0.001){
double j = i + 0.001;
double result1 = a*pow(i,3)+b*pow(i,2)+c*i+d;
double result2 = a*pow(j,3)+b*pow(j,2)+c*j+d;
if(result1*result2<0){
cnt++;
cout << fixed << setprecision(2) << j;
if(cnt!=3)
cout << " ";
else
break;
}
}
return 0;
}
但是刚开始暴力求解也WA了一半:( ,那会的做法是遍历后代入得到的式子结果为0的话就输出被遍历到的数。但是!!!!!!这里有一个非常严重的问题!如果double型的数据,判断等于0时的条件写成x==0,其实是不严谨的,它并不完全等于绝对的零,这会存在很大隐患,而这和浮点型数据在内存中的存储方式有关。通俗的讲就是浮点数是近似值,一般用精度判断。而一般情况下我们写==0是因为系统自动优化了。但这道题要求精确到小数点后两位,算是比较严格。
所以,像上述过程,我们可以优化代码为如下:
即判断浮点型数据是否为0,可用 if( fabs(x) < 1e-6 ) 通常满足这样的情况就可以认为是0了。
#include <iostream>
#include <cmath>
#include <iomanip>
using namespace std;
int main()
{
double a,b,c,d;
int cnt = 0;
cin >> a >> b >> c >> d;
for(double i = -100; i <=100.00; i+=0.001){
double result1 = a*pow(i,3)+b*pow(i,2)+c*i+d;
if(fabs(result1) < 1e-6){
cnt++;
cout << fixed << setprecision(2) << i;
if(cnt!=3)
cout << " ";
else
break;
}
}
return 0;
}
另:注意一下setprecision和fixed结合使用来控制精度的方法,以及其头文件。
2.盛金公式
当A=B=0时,方程有一个三重实根。
当Δ=B^2-4AC>0时,方程有一个实根和一对共轭复根。
当Δ=B^2-4AC=0时,方程有三个实根,其中有一个二重根。
当Δ=B^2-4AC<0时,方程有三个不相等的实根。
因为题目说了有三个不同实根,所以就套公式就行。
#include <iostream>
#include <cmath>
#include <iomanip>
using namespace std;
int main()
{
double a,b,c,d,A,B,C,T,si,i,j,k;
cin >> a >> b >> c >> d;
A = b*b-3*a*c;
B = b*c-9*a*d;
C = c*c-3*b*d;
T = (2*A*b-3*a*B)/(2*sqrt(A*A*A));
si = acos(T);
i = (-b-2*sqrt(A)*cos(si/3))/(3*a);
j = (-b+sqrt(A)*(cos(si/3)+sqrt(3)*sin(si/3)))/(3*a);
k = (-b+sqrt(A)*(cos(si/3)-sqrt(3)*sin(si/3)))/(3*a);
cout << fixed << setprecision(2) << i << " ";
cout << fixed << setprecision(2) << k << " ";
cout << fixed << setprecision(2) << j << " ";
return 0;
}
3.分治
思路都写在代码里的注释了,很详细。
#include <iostream>
#include <cmath>
#include <iomanip>
using namespace std;
double a,b,c,d;
double aim(double i){
return a*pow(i,3)+b*pow(i,2)+c*i+d;
}
int main()
{
int cnt = 0;
cin >> a >> b >> c >> d;
// 因为两根之差的绝对值大于等于1,所以存在根的区间可以是以1为间隔划分
for(double i = -100; i <= 100; i++){
// l和r记录区间左右端点
double l = i;
double r = l + 1;
double result1 = aim(l);
double result2 = aim(r);
// 如果左端点为零点直接输出,但是不能同时判断右端点,因为往后会重复判断
// 也不能判断只判断右端点,不然会漏掉第一个区间端点
if(!result1){
cnt++;
printf("%.2lf ",l);
continue;//区间端点为零点,因此该区间内不可能再有别的根,直接进行下一次循环
}
// 如果区间内存在根,开始二分逼近根
if(result1*result2 < 0){
// 题目要求精确到后两位,因此控制区间长度在0.001以内
while(r-l > 0.001){
double m = (l+r)/2;
// 如之前所言,浮点型是近似数,==0不代表为绝对零
if(result1*aim(m) <= 0){
r = m;
}else
l = m;
}
cnt++;
printf("%.2lf ",l);
}
if(cnt == 3)
break;
}
return 0;
}