1. 程式人生 > >PYC#1歡樂賽第三題題解

PYC#1歡樂賽第三題題解

第三題題目大意如下:

已知n條二次函式曲線Si(x)=aix^2+bix+ci(ai>=0),定義F(x)=max{Si(x)},求出F(x)在[0,1000]上的最小值。第一行為資料組數T。每組資料第一行位正整數n,以下n行每行包括3個整數a,b,c。對於每組資料,輸出所要求的最小值,保留4位小數。T< 10, n ≤ 10000,0 ≤ a ≤ 100,|b| ≤ 5000, |c| ≤5000。

有點像數學+二分,開始我想二分x的,但發現函式眾多,線索混亂,覺得這會有問題。
後來這道題經RXD大牛點撥,我有了一個絕妙的想法 :二分列舉答案f(x),並驗證。比如,我們列舉的答案當前是k,那麼我們在座標系裡劃一道y=k的影象,這與n條函式影象可能會有交點。因

F(x)=max{Si(x)},,所以在【0...1000】範圍內,y=k一定在最上面。設某一二次函式解析式y=ax^2+bx+c,它與直線的交點為方程ax^2+bx+c=k的根(因為0≤ a,所以如果b^2-4*a*c<0,說明函式在直線上方,可以直接否決當前的K),而它在直線的下方的部分可以用x1<x<x2來表示(x1和x2是方程的根)。如果對於所有的函式,其在直線下方的解集存在於【0..1000】範圍內(無論多少),說明當前的K是可行的。
時間效率:O(log2(P)*n)),其中p是你答案列舉的範圍。(最好設的大一點) 
然而,我交上去後,發現全WA了!! 後來經過我對資料的不斷除錯,發現如下兩個問題。
(1)二分答案時如何確定邊界。保留4位精度嘛,我原先用ok函式來判斷。如果ok(l)=ok(r),就退出。

long ok(double k)
{
  if (long(k*100000)%10>4) return(long(k*10000+1));
  return (long(k*10000));
}

即如果列舉的l和r小數點後四位相同,就退出。
但是這會有問題 ,比如l=0.999,並無限接近1;r=1.001,也無限接近1。這樣,即使二分到很後面,如l=0.99999999,r=1.00000001,他們的後四位仍然不同。
後來我發現一個更簡單的判斷方法,即(r-l<=0.00001 (5位)) 
(2)a可能為0,即某一函式可能不是二次函式,而是一次函式。
因為在求根公式中,a被當做了除數,這樣除0的話就會爆掉。因此,對於一次函式,要簡單處理一下(其實更容易)

以下附程式碼:


#include<stdio.h>
#include<cmath>
using namespace std;
long a[10001],b[10001],c[10001],i,j,t,n,oo=2000000000;                           //oo被視為無限大
double ans;
bool check(double h)
{
  long i;double x1,x2,t,start1=0,start2=1000,p;
  for (i=1;i<=n;i++)
  {
    p=b[i]*b[i]-4*a[i]*(c[i]-h);
    if (p<0) returnfalse;p=sqrt(p);
    if (a[i]==0)
    {
      if(b[i]>0){x1=-oo;x2=(h-c[i])/b[i];}
      else if(b[i]<0){x1=(h-c[i])/b[i];x2=oo;}
      else if (c[i]>h) return false;
      else { x1=-oo;x2=oo;}
    }
    else
    {
      x1=(-b[i]-p)/a[i]/2;
      x2=(-b[i]+p)/a[i]/2;
    }  
    if (start2<x1||x2<start1) returnfalse;
    if (x1>start1) start1=x1;if (x2<start2)start2=x2;
  }
  return true;
}
double erfen(double l,double r)
{
  double mid=(l+r)/2;
  if ((r-l)<=1e-5) return mid;
  if (check(mid)) return erfen(l,mid);
  return erfen(mid,r);
}
int main()
{
  freopen("pyc.in","r",stdin);
  freopen("pyc.out","w",stdout);
  scanf("%ld",&t);
  for (j=1;j<=t;j++)
  {
    scanf("%ld",&n);
    for (i=1;i<=n;i++)
     scanf("%ld%ld%ld",&a[i],&b[i],&c[i]);
    ans=erfen(0,10000);                         //當然再開大一點也可以,只是不要超時了
    printf("%.4f\n",ans);
  }
}
最後提出一個意見,第10組資料中出現了a<0,使我的程式出錯,請PYC及時糾正。O(∩_∩)O~~