【HDU4498】Function Curve-分段+自适应Simpson积分法
测试地址:Function Curve
题目大意:给定
被
做法:这一题需要用到自适应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;
}