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條函式影象可能會有交點。因
時間效率: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~~