• 计算几何 val.3


    计算几何 val.3

    自适应辛普森法

    可以用来求多边形的面积并(圆也行)

    定积分

    定积分的几何意义是函数的曲线上 (x) 的一段区间与 (x) 轴围成的曲边梯形的带符号面积

    表示法为

    [int_{a}^{b} f(x) mathrm{d} x ]

    引入

    计算方法:

    1. 分成一堆小区间

      [int_{a}^{b} f(x) mathrm{d} x=lim _{n ightarrow infty} sum_{i=1}^{n} frac{b-a}{n} fleft(a+frac{b-a}{n} i ight) ]

    2. 牛顿-莱布尼茨公式

      如果

      [F^{prime}(x)=f(x) ]

      [int_{a}^{b} f(x) mathrm{d} x=F(b)-F(a) ]

      这个可以求:(int_a^b(frac 1 x)dx = ln |b|-ln |a|)

      这也是连接定积分和不定积分的桥梁

    对于一些难求的积分,我们可以用数值积分来求,其中常用的是自适应辛普森积分

    辛普森公式

    此公式用二次函数来拟合原函数

    [int_{a}^{b} f(x) mathrm{d} x approx int_{a}^{b}left(A x^{2}+B x+C ight) mathrm{d} x ]

    [=frac{A}{3}left(b^{3}-a^{3} ight)+frac{B}{2}left(b^{2}-a^{2} ight)+C(b-a) ]

    [=frac{2 A(b-a)left(b^{2}+a b+a^{2} ight)+3 B(b+a)(b-a)+6 C(b-a)}{6} ]

    提出(b-a)

    [=frac{(b-a)left(2 A b^{2}+2 A a b+2 A a^{2}+3 B b+3 B a+6 C ight)}{6} ]

    [=frac{(b-a)left(A a^{2}+B a+C+A b^{2}+B b+C+A a^{2}+2 A a b+A b^{2}+2 B b+2 B a+4 C ight)}{6} ]

    [=frac{(b-a)left(f(a)+f(b)+A(a+b)^{2}+2 B(a+b)+4 C ight)}{6} ]

    [=frac{(b-a)left(f(a)+f(b)+4left(Aleft(frac{a+b}{2} ight)^{2}+Bleft(frac{a+b}{2} ight)+C ight) ight)}{6} ]

    [=frac{(b-a)left(f(a)+f(b)+4 fleft(frac{a+b}{2} ight) ight)}{6} ]

    于是可以得到公式:

    [int_{a}^{b} f(x) mathrm{d} x approx frac{(b-a)left(f(a)+f(b)+4 fleft(frac{a+b}{2} ight) ight)}{6} ]

    当然,对于二次函数这是对的

    对于其余情况,(b-a)越小,上面两个式子越接近

    这种情况下我们就要调整精度

    处理精度

    考虑把一段长的区间分成很多段小区间求和

    可是分的太少了不能满足精度要求,太多了会TLE

    那么考虑什么时候停止分下去呢?

    对于当前区间,求出(ans=simpson(l,r),mid=frac{l+r}{2})

    然后求出对于下一层区间的答案:(ls=simpson(l,mid),rs=simpson(mid,r))

    注意此处mid右边不用加一,不是整数域

    如果(|ls+rs-ans|<eps),即满足精度要求,可以停止二分

    考虑到一些小的误差加起来很大,eps要设的比题目要求的小一点

    而且下一层的eps是上一层的二分之一,因为有两个

    代码实现

    double F(...){
    	...
    }
    double simpson(double l,double r){
    	double mid=(l+r)/2.0;
    	return (r-l)/6.0*(F(l)+4.0*F(mid)+F(r));
    } 
    double solve(double l,double r,double ans,double eps){
    	double mid=(l+r)/2.0;
    	double ls=simp(l,mid),rs=simp(mid,r);
    	if(fabs(ls+rs-ans)<eps*15) return ls+rs+(ls+rs-ans)/15;
    	else return solve(l,mid,ls,eps*0.5)+solve(mid,r,rs,eps*0.5);
    }
    

    等一下,好像实现和上面的思路不同?

    if(fabs(ls+rs-ans)<eps*15) return ls+rs+(ls+rs-ans)/15;

    (15)是个啥东西?

    噔 噔 咚

    论证,请(绝望)

    最后移一下项就好了,得到ls+rs+(ls+rs-ans)/15​

    模板

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #define db double
    using namespace std;
    db a,b,c,d,l,r;
    db F(db x){
    	return (c*x+d)/(a*x+b);
    }
    db simp(db l,db r){
    	db mid=(l+r)/2.0;
    	return (r-l)/6.0*(F(l)+4.0*F(mid)+F(r));
    }
    db solve(db l,db r,db ans,db eps){
    	db mid=(l+r)/2.0;
    	db ls=simp(l,mid),rs=simp(mid,r);
    	if(fabs(ls+rs-ans)<eps*15) return ls+rs+(ls+rs-ans)/15;
    	else return solve(l,mid,ls,eps*0.5)+solve(mid,r,rs,eps*0.5);
    }
    int main(){
    	scanf("%lf%lf%lf%lf%lf%lf",&a,&b,&c,&d,&l,&r);
    	printf("%.6f",solve(l,r,simp(l,r),1e-8));
    	return 0;
    } 
    

    时间复杂度

    精度不能开太小,开要求精度再多2~3位都很稳

    练习

    找不到题啊。。

    BZOJ2178

    面积并:

    [S=int_l^rf(x)dx ]

    (f(x))为一条垂直于x轴的线的覆盖的长度

    然后就可以用辛普森积分算了

    (f)的话可以求出所有交点,按上点排序,O(n)枚举计算出下一条线是否和当前有交点,并计算长度

    90分代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    #define db double
    int n;
    const int N = 1001;
    db x[N],y[N],r[N];
    const double eps=1e-3;
    struct node{
    	db u,d;
    }p[N];
    int tp;
    int cmp(const node &aa,const node &bb){
    	return aa.u<bb.u;
    }
    db F(db pos){
    	tp=0;
    	for(int i=1;i<=n;i++){
    		if(pos>=x[i]-r[i]&&pos<=x[i]+r[i]){
    			p[++tp].u=y[i]-sqrt(r[i]*r[i]-(x[i]-pos)*(x[i]-pos));
    			p[tp].d=y[i]+sqrt(r[i]*r[i]-(x[i]-pos)*(x[i]-pos));
    		}
    	}
    	sort(p+1,p+tp+1,cmp);
    	db nu=p[1].u,nd=p[1].d,ans=0;
    	for(int i=2;i<=tp;i++){
    		if(p[i].u<=nd){
    			nd=max(nd,p[i].d);
    		}else{
    			ans+=(nd-nu);
    			nu=p[i].u,nd=p[i].d;
    		}
    	}
    	ans+=(nd-nu);
    	return ans;
    }
    db simp(db l,db r){
    	db mid=(l+r)*0.5;
    	return (r-l)/6.0*(F(l)+4.0*F(mid)+F(r)); 
    }
    db solve(db l,db r,db ans,db eps){
    	db mid=(l+r)*0.5;
    	db ls=simp(l,mid),rs=simp(mid,r);
    	if(fabs(ls+rs-ans)<15.0*eps) return ls+rs+(ls+rs-ans)/15.0;
    	else return solve(l,mid,ls,eps*0.5)+solve(mid,r,rs,eps*0.5);
    }
    int main(){
    	scanf("%d",&n);
    	db ml=1926081700.1,mr=-1926081700.1;
    	for(int i=1;i<=n;i++){   
    		scanf("%lf%lf%lf",&x[i],&y[i],&r[i]);
    		ml=min(x[i]-r[i],ml);
    		mr=max(mr,x[i]+r[i]);
    	}
    	printf("%.3f",solve(ml,mr,simp(ml,mr),eps));
    	return 0;
    }
    

    最后一个点被卡了,认识到此算法只能用来骗分。艹

    闵可夫斯基和

    空间中点集的和

    有一些性质,比如,凸包之间的闵可夫斯基和一定是凸包

    求凸包之间的闵可夫斯基和的方法:把两个凸包的每一条向量都抠出来,按照极角序排序构成新凸包

    实现方法:

        pot P={-inf,-inf},Q={-inf,-inf},R={-inf,-inf};
        n=read(); 
        for(int i=1;i<=n;i++)
        {
            a[i].x=read();a[i].y=read();
            if(dcmp(a[i].y-P.y)==0&&dcmp(a[i].x-P.x)<0)P=a[i];
            if(dcmp(a[i].y-P.y)>0)P=a[i];
            if(i!=1)f[++cnt]=a[i]-a[i-1];if(i==n)f[++cnt]=a[1]-a[i];
        }
        n=read();
        for(int i=1;i<=n;i++)
        {
            b[i].x=read();b[i].y=read();
            if(dcmp(b[i].y-Q.y)==0&&dcmp(b[i].x-Q.x)<0)Q=b[i];
            if(dcmp(b[i].y-Q.y)>0)Q=b[i];
            if(i!=1)f[++cnt]=b[i]-b[i-1];if(i==n)f[++cnt]=b[1]-b[i];
        }
        n=read();
        for(int i=1;i<=n;i++)
        {
            c[i].x=read();c[i].y=read();
            if(dcmp(c[i].y-R.y)==0&&dcmp(c[i].x-R.x)<0)R=c[i];
            if(dcmp(c[i].y-R.y)>0)R=c[i];
            if(i!=1)f[++cnt]=c[i]-c[i-1];if(i==n)f[++cnt]=c[1]-c[i];
        }
        sort(f+1,f+cnt+1,cmp);
        pot k=P+Q+R;p[++tot]=k;
        for(int i=1;i<=cnt;i++)
        {
            k=k+f[i];
            if(i!=cnt&&dcmp(f[i].x*f[i+1].y-f[i].y*f[i+1].x)==0)continue;
            p[++tot]=k;
        }
        tot--;k=p[1];
    

    没有例题,抱歉

    Pick定理

    结论

    在一个平面直角坐标系内,以整点为顶点的简单多边形,设其内部整点数为(a),边上(包括顶点)的整点数为(b),则它的面积为(a+frac b 2 -1)

    证明

    例题

    =模板

    边上的格点数=|dx|和|dy|的最大公约数

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    using namespace std;
    int ol,x1,x2,x3,ya,yb,yc;
    int gcd(int x,int y) {
    	return y==0?x:gcd(y,x%y);
    }
    int area() {
    	return abs((x2-x1)*(yc-ya)-(x3-x1)*(yb-ya))/2;
    }
    int cal(int x1,int ya,int x2,int yb) {
    	int dx,dy;
    	if(x1<x2)dx=x2-x1;
    	else dx=x1-x2;
    	if(ya<yb)dy=yb-ya;
    	else dy=ya-yb;
    	return gcd(dx,dy);
    }
    int main() {
    	while(scanf("%d%d%d%d%d%d",&x1,&ya,&x2,&yb,&x3,&yc)) {
    		if(!x1&&!x2&&!x3&&!ya&&!yb&&!yc)break;
    		ol=cal(x1,ya,x2,yb)+cal(x2,yb,x3,yc)+cal(x3,yc,x1,ya);
    		printf("%d
    ",area()-ol/2+1);
    	}
    	return 0;
    }
    

    后记

    其实 val.2 比 val.3 难且重要

    但是不重要不代表不学呀

    辛普森积分还是挺实用的,我觉得

    没有val.4了,最多写写做题记录

  • 相关阅读:
    SpringBoot条件注解@Conditional
    IDEA远程Debug
    聊一聊Java如何接入招行一网通支付功能
    IDEA中使用lombok插件
    使用Java类加载SpringBoot、SpringCloud配置文件
    Java项目启动时执行指定方法的几种方式
    Java定时任务解决方案
    04 Python并发编程(守护进程,进程锁,进程队列)
    03 初识并发编程
    02 网络编程协议(TCP和UDP协议,黏包问题)以及socketserver模块
  • 原文地址:https://www.cnblogs.com/lcyfrog/p/11712272.html
Copyright © 2020-2023  润新知