一元三次方程求解(數學、二分)


 

https://www.luogu.com.cn/problem/P1024

 

Description

有形如:ax 3+bx 2+c x+d=0 這樣的一個一元三次方程。
給出該方程中各項的系數(a,b,c,d 均為實數),並約定該方程存在三個不同實根(根的范圍在-100至100之間),且根與根之差的絕對值>=1。要求三個實根。

Input

四個實數:a,b,c,d

Output

由小到大依次在同一行輸出這三個實根(根與根之間留有空格),並精確到小數點后2位

Sample Input

1 -5 -4 20

Sample Output

-2.00 2.00 5.00
 

HINT

數據規模和約定
|a|,|b|,|c|,|d|<=10

 

 

記方程f(x)=0,若存在2個數x1x2,且x1<x2f(x1)×f(x2)<0,則在(x1,x2)之間一定有一個根。

這題解法比較多

 

 

因為區間很大,所以可以二分。

題目保證三個答案都在[-100,100]范圍內,且兩個根的差的絕對值>=1,所以每一個大小為1的區間里至多有1個解。

當區間的兩個端點的函數值異號時區間內一定有一個解,這個時候我們可以在區間內進行二分查找ans,如果同號則該區間一定沒有解。

 

剛開始遍歷i從-10000到10000了,然后i再除以100當成x的值,沒想到這樣顯示TLE了。。。

 

 1 #include <stdio.h>
 2 #include <iostream>
 3 #include <string>
 4 #include <string.h>
 5 #include <algorithm>
 6 #include <map>
 7 #include <vector>
 8 #include <set>
 9 #include <stack>
10 #include <queue>
11 #include <math.h>
12 #include <sstream>
13 using namespace std;
14 typedef long long LL;
15 const int INF=0x3f3f3f3f;
16 const int maxn=1e5+10;
17 const double eps = 1e-8;
18 const double PI = acos(-1);
19 
20 double a,b,c,d;//開成double吧,免得出錯 
21 vector<double> ans;
22 double f(double x)
23 {
24     return a*x*x*x+b*x*x+c*x+d;
25 }
26 
27 double solve(double x1,double x2)    //在x1和x2中二分找答案 
28 {
29     double L=x1,R=x2;
30     while(R-L>1e-4)    //這里的eps一般要比題中要求的精度(1e-2)多兩位。
31     {
32         double mid=(L+R)/2;
33         double t1=f(L);
34         double t2=f(mid);
35         double t3=f(R);
36         if(t2==0) return mid;
37         if(t1*t2<0) R=mid;
38         else if(t2*t3<0) L=mid;
39     }
40     return (L+R)/2;//返回L,R,(L+R)/2都行
41 }
42 
43 int main()
44 {
45     #ifdef DEBUG 
46     freopen("sample.txt","r",stdin);
47     #endif
48     
49     scanf("%lf %lf %lf %lf",&a,&b,&c,&d);
50     double pre=f(-100.0);
51     for(int i=-100;i<=100;i++)
52     {
53         double t=f(i);
54         if(t!=0)
55         {
56             if(t*pre<0) ans.push_back(solve(i-1,i));
57         }
58         else ans.push_back(i);
59         pre=t;
60     }
61     for(int i=0;i<3;i++)
62         printf(i==2?"%.2f\n":"%.2f ",ans[i]);
63     
64     return 0;
65 }

 

下面是我看別的大佬寫的

 

盛金公式:

 

 

 

 

 1 #include <iostream>
 2 #include <math.h>
 3 #include <iomanip>
 4 using namespace std;
 5 int main()
 6 {
 7      double a,b,c,d;
 8      double as,bs,t,si;
 9      double x1,x2,x3;
10      cin>>a>>b>>c>>d;
11      as=b*b-3*a*c;
12      bs=b*c-9*a*d;
13      t=(2*as*b-3*a*bs)/(2*sqrt(as*as*as));
14      si=acos(t);
15      x1=(-b-2*sqrt(as)*cos(si/3))/(3*a);
16      x2=(-b+sqrt(as)*(cos(si/3)+sqrt(3)*sin(si/3)))/(3*a);
17      x3=(-b+sqrt(as)*(cos(si/3)-sqrt(3)*sin(si/3)))/(3*a);
18      cout<<fixed<<setprecision(2)<<x1<<" ";
19      cout<<fixed<<setprecision(2)<<x3<<" ";
20      cout<<fixed<<setprecision(2)<<x2<<" ";
21      return 0;
22 }

 

最后是EarthGiao大佬寫的題解:

導數+勘根定理+牛頓迭代.

先對函數求導,f'(x)=3ax^2+2*bx+c.

然后直接求根公式求f'(x)=0的點,也就是函數極點.

這題保證有三個不定根,所以有兩個單峰.

我們分別設這兩個點為p,q.

然后顯然的必有三個根在[-100,p),[p,q],(q,100]三個區間內 (兩極點間必定存在零點,勘根定理).

然后用神奇的牛頓迭代法多次迭代就好了.

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cmath>
 4 #define eps 1e-4
 5 using namespace std;
 6 double x1,x2,x3,a,b,c,d;
 7 double f(double x){return a*x*x*x+b*x*x+c*x+d;}
 8 double df(double x){return 3*a*x*x+2*b*x+c;}
 9 double slove(double l,double r)
10 {
11     double x,x0=(l+r)/2;
12     while(abs(x0-x)>eps)
13       x=x0-f(x0)/df(x0),swap(x0,x);
14     return x;
15 }
16 int main()
17 {
18     cin>>a>>b>>c>>d;
19     double p=(-b-sqrt(b*b-3*a*c))/(3*a);
20     double q=(-b+sqrt(b*b-3*a*c))/(3*a);
21     x1=slove(-100,p),x2=slove(p,q),x3=slove(q,100);
22     printf("%.2lf %.2lf %.2lf",x1,x2,x3);
23     return 0;
24 }

 

 

 

 

 

 

 

 

-


免責聲明!

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



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