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

测试地址: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-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;
}
posted @ 2017-07-01 11:51  Maxwei_wzj  阅读(143)  评论(0编辑  收藏  举报