1. 程式人生 > >一元三次方程 (codevs 1038)題解

一元三次方程 (codevs 1038)題解

【問題描述】

     有形如:ax3+bx2+cx+d=0這樣的一個一元三次方程。給出該方程中各項的係數(a,b,c,d均為實數),並約定該方程存在三個不同實根(根的範圍在-100至100之間),且根與根之差的絕對值>=1。要求由小到大依次在同一行輸出這三個實根(根與根之間留有空格),並精確到小數點後2位。提示:記方程f(x)=0,若存在2個數x1和x2,且x1<x2,f(x1)*f(x2)<0,則在(x1,x2)之間一定有一個 根。

【樣例輸入】

    1 -5 -4 20

【樣例輸出】

   -2.00 2.00 5.00

【解題思路】

     本題為NOIP2001 提高組第二題,可用兩種方法解決,列舉/二分,但老師改了一下資料,將保留2位改為了保留4位,導致列舉會超時,無奈之下用了二分……這裡還是講保留2位小數。題目中的提示已經完完全全告訴了我們思路,設f(x)=a*x^3+b*x^2+c*x+d,若用列舉,從-100開始,每次加0.01,按題目說明,如果f(x-0.005)*f(x+0.005)<0,那麼,x即是答案(題目要求保留兩位小數,只需write的時候加上:0:2即可,無需再判斷x),或者f(x)本身就等於0,x也是答案。詳見程式碼。

   下面為二分題解。

      二分是由分治策略引申的一種解題方法,比列舉會更快一些,所謂二分,就是將題目從中間分為兩個部分,再對左右兩邊的分問題進行解答,將最終答案遞歸回來即可。

      提示又給了我們很好的思路,這就是典型的二分了。如果f(x1)*f(x2)<0或者x1+1>x2,那麼就從中間分為兩個部分,為什麼是或者呢?題目中說根與根的差的絕對值>=1,因此,如果我們找的兩個數的差大於1,那麼有可能這兩個數中間不止存在一個解,因此也需要二分,利用這個特性,我們可以之間將-100與100二分。終止條件:(f(x)=0)or((x1+0.001>x2)and(f(x1)*f(x2)<0)這裡的終止條件與列舉不同,但原理是一樣的同樣是因為需要保留兩位小數,因此需要判斷三位。詳見程式碼。

【程式碼實現】

var a,b,c,d,x:real;
    w:integer;
function f(x1:real):real;
begin
 f:=a*x1*x1*x1+b*x1*x1+c*x1+d;
end;
begin
 x:=-100;
 readln(a,b,c,d);
 repeat
  x:=x+0.01;
  if (f(x)=0) or (f(x-0.005)*f(x+0.005)<0) then//注意這裡的條件,有兩種情況都算作根
   begin
    write(x:0:2,'  ');
    inc(w);
   end;
 until w=3;
end.
列舉法
 1
var a,b,c,d:double; 2 i:longint; 3 function js(x:double):double; 4 begin 5 js:=a*x*x*x+b*x*x+c*x+d; 6 end; 7 procedure try(x1,x2:double); 8 var x0,y0,y1,y2:double; 9 begin 10 x0:=(x1+x2)/2; 11 y0:=js(x0);y1:=js(x1);y2:=js(x2); 12 if (y0=0)or((x1+0.001>x2)and(y1*y2<0))then//滿足條件,輸出 13 begin 14 write(x0:0:2,' '); 15 exit; 16 end; 17 if (y1*y0<0)or(x0-x1>1) then//注意題中說了兩個解之間的差的絕對值>=1,所以遞迴的條件不只是單純的f(x1)*f(x0)<0 18 try(x1,x0); 19 if (y0*y2<0)or(x2-x0>1) then 20 try(x0,x2); 21 end; 22 begin 23 readln(a,b,c,d); 24 try(-100,100); 25 end.
二分法