poj 3737 UmBasketella(数学推导||三分)

题目:http://poj.org/problem?id=3737

我是想练一练三分的,听说这题能用上,但是我做完了也没把三分派上用场(第一种方法)。数学分析一下就能得出结果啊。令圆锥的底面半径是r,高是h,侧面母线是l,由几何知识:圆锥侧面积s1=2*pi*r*l/2=pi*r*l,底面积是s2=pi*r^2。所以s=pi*r*l+pi*r^2. V=(pi*r^2)/3*h. 同时有:l^2=r^2+h^2. 由它们可以得到:V=sqrt(s^2*r^2-2*pi*s*r^4)/3. 设f=(3V)^2=s^2*r^2-2*pi*s*r^4. 对它r一阶求导,f'=2sr(s-4pir^2). 当f'=0时,4*pi*r^2=s,此时达到极大值,又h^2=l^2-r^2=[(s-pi*r^2)/(pi*r)]^2-r^2,即h^2=(3/4)^2*s*4/pi-s/4/pi. V的公式也可以化成1/3*s/4*h。

#include <iostream>
#include<cstdio>
#include<cmath>
using namespace std;
const double pi=3.1415926;
double r,h,v,s;
int main()
{
    while(cin>>s){
        r=sqrt(s/4/pi);
        h=sqrt(0.75*0.75*4*s/pi-s/(4*pi));
        v=s*h/12;
        printf("%.2lf\n%.2lf\n%.2lf\n",v,h,r);
    }
    return 0;
}

用三分解决的思路:
针对二次函数或有极值的曲线函数三分搜索,找出那个极值点,最终求出问题的结果。

对比不同mid作为r计算得到的V,不断缩小区间,最后找出极值点,详见代码:
#include <iostream>
#include<cstdio>
#include<cmath>
using namespace std;
const double pi=acos(-1.0);
double s;
double cal(double r){
    double l=(s-pi*r*r)/pi/r;
    double h=sqrt(l*l-r*r);
    double v=pi*r*r*h/3;
    return v;
}
double ternarysearch(double low,double high){
     while(high-low>1e-7){
        double mid1=low+(high-low)/3,mid2=high-(high-low)/3;
        double v1=cal(mid1),v2=cal(mid2);
        if(v2-v1>1e-9)low=mid1;
        else high=mid2;
     }
     return low;
}
int main()
{
    while(cin>>s){
        double r,h,l,v;
        r=ternarysearch(0,sqrt(s/pi));
        l=(s-pi*r*r)/pi/r;
        h=sqrt(l*l-r*r);
        v=pi*r*r*h/3;
        printf("%.2lf\n%.2lf\n%.2lf\n",v,h,r);
    }
    return 0;
}


你可能感兴趣的:(数学,poj,三分搜索)