【题目描述】
给出N个圆,求其面积并
【输入】
先给一个数字N ,N< = 1000
接下来是N行是圆的圆心,半径,其绝对值均为小于1000的整数
【输出】
面积并,保留三位小数
辛普森积分实在太强了……
它相当于用很多1、2、3次函数去拟合一个函数以达到轻松计算积分的目的。
这里给一篇博客,讲的很好:一篇博客
然后有一些小小的优化:
为了解决函数不连续的问题,可以设置一个限定长度,让辛普森积分只在值域范围在限定长度以内的情况下才停止递归。
然后有一个精度优化,辛普森的eps可以设置成每向下递归一层就除以2,效果拔群。
#include<cstdio> #include<algorithm> #include<iostream> #include<cmath> using namespace std; const int maxn = 1e3 + 5; struct Circle { double x, y, R; }cir[maxn]; struct Itv { double b, t; bool operator < (const Itv & A) const { return b < A.b; } }itv[maxn]; int n, cnt; double dx, tmp, L = 1e300, R = -1e300; #define sqr(x) ((x) * (x)) double F(double x) { cnt = 0; double gu, mxT = -1e300, mxB = -1e300, ans = 0; for(register int i = 1; i <= n; ++i) { if((tmp = abs(cir[i].x - x)) > cir[i].R) continue; gu = sqrt(sqr(cir[i].R) - sqr(tmp)); itv[++cnt] = (Itv){cir[i].y - gu, gu + cir[i].y}; } sort(itv + 1, itv + cnt + 1); for(register int i = 1; i <= cnt; ++i) { if(mxT < itv[i].b) ans += mxT - mxB, mxB = itv[i].b, mxT = itv[i].t; else mxT = max(mxT, itv[i].t); } ans += mxT - mxB; return ans; } double S(double L, double R, double FL, double FR, double FM) { return (R - L) * (FL + 4 * FM + FR) / 6.0; } double Simpson(double L, double R, double FL, double FM, double FR, double eps) { double mid = (L + R) / 2.0, nans = S(L, R, FL, FR, FM); double FLM = F((L + mid) / 2.0), FRM = F((mid + R) / 2.0); if(R - L <= 1) if(abs(S(L, mid, FL, FM, FLM) + S(mid, R, FM, FR, FRM) - nans) <= 15 * eps) return nans; nans = Simpson(L, mid, FL, FLM, FM, eps / 2) + Simpson(mid, R, FM, FRM, FR, eps / 2); return nans; } int main() { scanf("%d", &n); for(register int i = 1; i <= n; ++i) { scanf("%lf%lf%lf", &cir[i].x, &cir[i].y, &cir[i].R); L = min(L, cir[i].x - cir[i].R); R = max(R, cir[i].x + cir[i].R); } printf("%.3lf", Simpson(L, R, F(L), F((L + R) / 2.0), F(R), 1e-5)); return 0; }
没有帐号? 立即注册