链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1345

题意:给两个无限高的圆柱的半径,它们垂直放置,坐标轴相交,求相交部分的体积。

思路:数值积分,用辛普森公式来算。把三重积分转化为一重的。

假设半径为r1的圆柱沿y轴,r2的圆柱沿z轴,那么从x轴看过去,相交部分在yoz平面的切面是个矩形。

矩形在y轴方向的长度为2*y=sqrt(r12 -x2),在z轴方向上的长度为2*sqrt(r2- x2),这样积分算出来的体积是yoz平面上半部分的体积,还有下半部分的,总的体积要乘以2.

如果是从z方向看过去,则平行于xOy平面所做的切面都是一个矩形,x方向长2*sqrt(r1*r1-z*z),y方向长2*sqrt(r2*r2-z*z),同样总体积需要乘以2.

 

由高数知识可知: 有下面的三重积分成立

∫∫∫dxdydz =|Ω|  (Ω为积分区域,|Ω|为区域Ω的体积) 。所以关键在于把Ω表示出来。参考下面的图形:

 

  列出两个圆柱的方程:

  x2 + y2 = r12          y=sqrt(r12 -x2)

  x2 + z2 = r22          z=sqrt(r2- x2)

  则 ∫∫∫dxdydz = ∫dx ∫∫dydz = ∫dx ∫ 2*sqrt(r12 -x2) * 2*sqrt(r2- x2) dydz = 2* ∫min( r1 , r2 )  2*2* sqrt((r12 -x2)*(r22 - x2)) dx ,  

  其中 x ∈ [ 0 , min(r1, r2) ]       是积分yz还是xy还是xz都可以。

 

下面是zsl学长的代码,使用的是变步长的simpson积分

#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;

#define db double
#define rt return
#define cs const

cs db eps = 1e-9;
inline int sig(db x){rt (x>eps)-(x<-eps);}

db r1, r2;

inline db f(db x) {
    rt 4. * sqrt(r1*r1-x*x) * sqrt(r2*r2-x*x);
}

inline db simpson(db a, db b) {
    rt (b-a)*(f(a) + 4.*f((a+b)/2.) + f(b)) / 6.;
}

inline db calc(db a, db b, int depth) {
    db total = simpson(a, b), mid = (a+b) / 2.;
    db tmp = simpson(a, mid) + simpson(mid, b);
    if(sig(total-tmp) == 0 && depth > 3) rt total;
    rt calc(a, mid, depth + 1) + calc(mid, b, depth + 1);
}

int main() {
//    freopen("std.in", "r", stdin);
    int T;
    scanf("%d", &T);
    while(T--) {
        scanf("%lf%lf", &r1, &r2);
        if(sig(r1-r2) > 0) swap(r1, r2);
        printf("%.4lf\n", 2. * calc(0., r1, 0));
    }
    rt 0;
}
View Code


这是我的代码,自适应simpson积分

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
typedef long long LL;
using namespace std;
const double PI=acos(-1);
const double eps=1e-4;
const int maxn=100;
double r1,r2;

double F(double x)
{
    return 8.*sqrt((r1*r1-x*x)*(r2*r2-x*x));
}
double simpson(double a,double b)
{
    double m=a+(b-a)/2;
    return (b-a)*(F(a)+4*F(m)+F(b))/6;
}
double asr(double a,double b,double e,double A)
{
    double m=a+(b-a)/2;
    double L=simpson(a,m),R=simpson(m,b);
    if(fabs(L+R-A)<=15*e) return L+R+(L+R-A)/15.;
    return asr(a,m,e/2,L)+asr(m,b,e/2,R);
}
double asr(double a,double b,double e)
{
    return asr(a,b,e,simpson(a,b));
}
int main()
{
//    freopen("in.cpp","r",stdin);
    int n;
    scanf("%d",&n);
    while(n--)
    {
        scanf("%lf%lf",&r1,&r2);
        if(r1>r2) swap(r1,r2);
        printf("%.4lf\n\n",asr(0,r1,eps));
    }

    return 0;
}
View Code


注意:simpson积分不太稳定,所以精度eps最好比要求的精度高几级,但是要注意不要超时。

 

自适应simpson积分模板

double F(double x)
{
    。。。
}
double simpson(double a,double b)
{
    double m=a+(b-a)/2;
    return (b-a)*(F(a)+4*F(m)+F(b))/6;
}
double asr(double a,double b,double e,double A)
{
    double m=a+(b-a)/2;
    double L=simpson(a,m),R=simpson(m,b);
    if(fabs(L+R-A)<=15*e) return L+R+(L+R-A)/15.;
    return asr(a,m,e/2,L)+asr(m,b,e/2,R);
}
double asr(double a,double b,double e)
{
    return asr(a,b,e,simpson(a,b));
}
View Code

变步长的simpson积分模板

 

double f(double x)
{
    ...
}

double simpson(double a, doubleb) {
    return (b-a)*(f(a) + 4.*f((a+b)/2.) + f(b)) / 6.;
}

double calc(double a, double b, int depth) {
    double total = simpson(a, b), mid = (a+b) / 2.;
    double tmp = simpson(a, mid) + simpson(mid, b);
    if(fabs(total-tmp)<eps && depth > 3) return total;
    return calc(a, mid, depth + 1) + calc(mid, b, depth + 1);
}
View Code

 

同样可以就几分的还有龙贝格求积分,勒让德-高斯求积分法,以后再去看看这两个。

 

 

 

 posted on 2013-07-22 21:53  ∑求和  阅读(542)  评论(0编辑  收藏  举报