洛谷-P1024-一元三次方程求解-普及-暴力/盛金公式/分治

一 题目与示例

二 题解

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值