一元三次方程求解


這是洛谷P1024,先上題目。

題目描述

有形如:a x^3 + b x^2 + c x + d = 0 這樣的一個一元三次方程。給出該方程中各項的系數(a,b,c,d均為實數),並約定該方程存在三個不同實根(根的范圍在 -100 至 100之間),且根與根之差的絕對值 1。要求由小到大依次在同一行輸出這三個實根(根與根之間留有空格),並精確到小數點后2位。

提示:記方程 f(x) = 0f(x)=0,若存在 22 個數 x_1x1 和 x_2x2,且 x_1 < x_2x1<x2f(x_1) \times f(x_2) < 0f(x1)×f(x2)<0,則在 (x_1, x_2)(x1,x2) 之間一定有一個根。

輸入格式

一行,4 個實數 a, b, c, da,b,c,d。

輸出格式

一行,3個實根,從小到大輸出,並精確到小數點后 2位。

輸入輸出樣例

輸入 #1
1 -5 -4 20
輸出 #1
-2.00 2.00 5.00

開始毫無頭緒,看了標簽后,發現是二分,只要找到一個大於零的數,再找到一個小於零的數,分別作為最左值和最右值,二分就能得答案,但問題又出現了,一元三次方程最多有三個解,即使找到了大於和小於零的數,也無法排除其他部分,最開始我想到的是在其余部分再做尋找,直到找到三個解的范圍,但這樣不就和從前往后捋一樣了嗎?於是我便從網上查了一下一元三次方程,結果根據導數可以確定一元三次方程單調遞增、遞減的范圍,而每個范圍中的解至多一個,所以在每個范圍中二分求解即可。還有的小問題在代碼中注釋。

下面上代碼。

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
double a,b,c,d;
double san(double n){
    return a*n*n*n+b*n*n+c*n+d;
}
double derta(double x,double y,double z){
    return y*y-4*a*c;
}
void erfen(double l,double r){
    double mid=(l+r)/2; 
    while(r-l>0.004){//最左值和最右值差0.004是因為要保留兩位小數
        if(san(mid)>0){
            r=mid;
        }
        else if(san(mid)<0){
            l=mid;
        }
        else{
            break;
        }
        mid=(l+r)/2;
    }
    printf("%.2lf ",mid);
}
//兩個二分是因為不確定單調遞增還是單調遞減
void erfen2(double l,double r){
    double mid=(l+r)/2;
    while(r-l>0.004){
        if(san(mid)>0){
            l=mid;
        }
        else if(san(mid)<0){
            r=mid;
        }
        else{
            break;
        }
        mid=(l+r)/2;
    }
    printf("%.2lf ",mid);
}
int main(){
    cin>>a>>b>>c>>d;
    double l1=-100,l2=-100,l3=-100,r1=100,r2=100,r3=100;
    if(derta(3*a,2*b,c)<=0){//f(x)=ax^3+bx^2+cx+d的導數是f`(x)=3ax^2+2bx+c
        double l=-100.0,r=100.0;
        if(a>0)erfen(l,r);//a的正負決定遞增遞減
        else erfen2(l,r);
    }
    else{
        double l=-100.0,r=(-2*b-sqrt(derta(3*a,2*b,c)))*1.0/(6*a);
        if(san(r)>=0){
            if(a>0)erfen(l,r);
            else erfen(l,r);
        }
        l=(-2*b-sqrt(derta(3*a,2*b,c)))*1.0/(6*a);
        r=(-2*b+sqrt(derta(3*a,2*b,c)))*1.0/(6*a);
        if(san(r)<0&&san(l)>0){//單調遞增和單調遞減分界處為解時,只取一次
            if(a>0)erfen2(l,r);
            else erfen(l,r);
        }
        l=(-2*b+sqrt(derta(3*a,2*b,c)))*1.0/(6*a);
        r=100.0;
        if(san(l)<=0){
            if(a>0)erfen(l,r);
            else erfen2(l,r);
        }
    }
    return 0;
}

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM