【HDU4498】Function Curve-分段+自适应Simpson积分法

来源:互联网 发布:优易淘宝小号注册机 编辑:程序博客网 时间:2024/05/22 09:51

测试地址:Function Curve
题目大意:给定n(1n50)(ki,ai,bi),要求函数曲线:

min(100,min{ki(xai)2+bi|0<in})

x=0x=100所截出的长度。
做法:这一题需要用到自适应Simpson积分法+分段。
我们知道,当两个函数曲线之间存在交点时,那么当经过这个交点时,确定最小值的函数可能变化,而不经过交点时确定最小值的函数则不会变化,这启发我们将函数分段来求。
首先把所有函数化成y=Ax2+Bx+C的形式(除了给出的n个函数外,还有一个y=100),我们发现n很小,所以我们暴力求出函数两两之间的交点(只需求出横坐标,用二次函数求根公式即可),然后将这些交点排序,那么相邻的两个交点之间就是一段了。这时候要注意,如果一个交点横坐标小于0或者大于100,就将它舍弃。然后对于每一段,找到这一段中确定最小值的函数(只需要把中点带进去,找到最小值所在的函数即可),然后就可以计算这个函数曲线在这一段上的长度了。
关于计算函数曲线y=f(x)在区间[a,b]上的长度,有这样一个公式:
len=ba1+f(x)2dx

其中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-6using 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;}
原创粉丝点击