测试地址:Function Curve
题目大意:给定
n(1≤n≤50)
组
(ki,ai,bi)
,要求函数曲线:
min(100,min{ki(x−ai)2+bi|0<i≤n})
被 x=0 和 x=100 所截出的长度。
做法:这一题需要用到自适应Simpson积分法+分段。
我们知道,当两个函数曲线之间存在交点时,那么当经过这个交点时,确定最小值的函数可能变化,而不经过交点时确定最小值的函数则不会变化,这启发我们将函数分段来求。
首先把所有函数化成 y=Ax2+Bx+C 的形式(除了给出的 n 个函数外,还有一个
关于计算函数曲线 y=f(x) 在区间 [a,b] 上的长度,有这样一个公式:
len=∫ba1+f′(x)2−−−−−−−−√dx
其中 f′(x) 为 f(x) 的导数。
那么显然 Ax2+Bx+C 的导数是 2Ax+B ,将这个值带进去就可以用自适应Simpson积分法来计算定积分了。
以下是本人代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define epss 1e-6
using namespace std;
int T,n,tot;
double a[60],b[60],c[60],p[5010];
double A,B,ans;
bool cmp(double a,double b)
{
return a<b;
}
void solve(double A,double B,double C,double &x1,double &x2)
{
if (A==0&&B==0) {x1=x2=-1;return;}
if (A==0) {x1=-C/B;x2=-1;return;}
double delta=B*B-4*A*C;
if (delta>=0)
{
x1=(-B+sqrt(delta))/(2*A);
x2=(-B-sqrt(delta))/(2*A);
if (fabs(x1-x2)<epss) x2=-1;
}
else x1=x2=-1;
}
double f(double x)
{
return sqrt(1+(2*A*x+B)*(2*A*x+B));
}
double calc(double a,double b)
{
double mid=(a+b)/2;
return (b-a)/6*(f(a)+f(b)+4*f(mid));
}
double simpson(double a,double b,double eps)
{
double mid=(a+b)/2,s1=calc(a,b),s2=calc(a,mid),s3=calc(mid,b);
if (fabs(s1-s2-s3)<=15*eps) return s2+s3+(s1-s2-s3)/15;
else return simpson(a,mid,eps/2)+simpson(mid,b,eps/2);
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
a[0]=0,b[0]=0,c[0]=100;
for(int i=1;i<=n;i++)
{
double k,x,y;
scanf("%lf%lf%lf",&k,&x,&y);
a[i]=k;
b[i]=-2*k*x;
c[i]=k*x*x+y;
}
tot=2;
p[1]=0,p[2]=100;
for(int i=0;i<=n;i++)
for(int j=i+1;j<=n;j++)
{
double x1,x2;
solve(a[i]-a[j],b[i]-b[j],c[i]-c[j],x1,x2);
if (x1>=0) p[++tot]=x1;
if (x2>=0) p[++tot]=x2;
}
sort(p+1,p+tot+1,cmp);
ans=0;
for(int i=1;i<tot;i++)
{
double l=p[i],r=p[i+1],mid=(l+r)/2,Min=1000;
int mini;
if (r>100.0) break;
for(int j=0;j<=n;j++)
if (a[j]*mid*mid+b[j]*mid+c[j]<Min)
{
Min=a[j]*mid*mid+b[j]*mid+c[j];
mini=j;
}
A=a[mini],B=b[mini];
ans+=simpson(l,r,epss);
}
printf("%.2lf\n",ans);
}
return 0;
}