zoukankan      html  css  js  c++  java
  • 「凸包」学习笔记

    • 前言

      未经允许,请勿转载。

      凸包真难。

      代码如果相似是因为我就是向别人学的。

      (为什么没人讲向量..第一次学不懂向量然后代码硬是看不懂嘤嘤嘤..)


    • 向量及运算的芝士(可能摘自很多地方..

      定义:有大小方向的量,又称为矢量

      坐标(A(x_1,y_1),B(x_2,y_2))

      向量(overrightarrow{AB}=(x_2-x_1,y_2-y_1))

      加法:((x_1+x_2,y_1+y_2))

      减法:((x_1-x_2,y_1-y_2))

      点积:(a·b=x_1x_2+y_1y_2)

      1. (a·b=0)(a perp b)
      2. (a·b<0) 则他们的夹角大于90
      3. (a·b>0) 则他们的夹角小于90

      叉积:(a×b=x_1y_2-x_2y_1)

      1. (a×b=0)(a)(b)共线(可以反向)
      2. (a×b>0)(b)(a)逆时针方向
      3. (a×b<0)(b)(a)顺时针方向

    • 有啥用?求什么

      给出一堆点,再用一根绳子包住,这就是个凸包啦。

      然后一般问绳子的长度。


    • 如何实现?

      主要分为2部:求上半部分和下半部分。

      首先,先对每一个点以(x)坐标从小到大排序,如果一样再以(y)坐标从小到大排序。

      比如上面那个例子:

      初始化((1)(2))放入栈中,此时栈中有((1)(2))

      然后把((3))放入栈中,此时栈中有((1)(2))

      因为此时在求下半部分,因此((3))我们希望越下越好,即(overrightarrow{(1)(2)}×overrightarrow{(2)(3)} geqslant0),(叉积的定义),然后可以发现((3))满足的:

      所以栈不改变,栈中有((1)(2)(3))

      然后,把((4))放入栈中,((4))满足(overrightarrow{(2)(3)}×overrightarrow{(3)(4)} geqslant0),所以栈不改变:((1)(2)(3)(4))

      然后把((5))放入栈中,发现((5))不满足(overrightarrow{(3)(4)}×overrightarrow{(4)(5)} geqslant0),所以把((4))弹出栈

      而出栈后发现,((5))不满足(overrightarrow{(2)(3)}×overrightarrow{(3)(5)} geqslant0),所以把((3))踢出栈。此时栈中剩下:((1)(2)(5))

      ((6))放入栈,((6))满足(overrightarrow{(2)(5)}×overrightarrow{(5)(6)} geqslant0),不改变栈:((1)(2)(5)(6))

      ((7))放入栈,((7))满足(overrightarrow{(5)(6)}×overrightarrow{(6)(7)} geqslant0),不改变栈:((1)(2)(5)(6)(7))

      注:可能画歪了..((6)(7))(y)坐标相同

      ((8))放入栈,((8))不满足(overrightarrow{(6)(7)}×overrightarrow{(7)(8)} geqslant0),所以把((7))踢出((1)(2)(5)(6)(8))

      但是发现,此时仍不满足(overrightarrow{(5)(6)}×overrightarrow{(6)(8)} geqslant0),所以再把((6))踢出,栈中剩下:((1)(2)(5)(8))

      这样,下部分的凸包就完成啦,而上部分反着做一遍就好了。


    • 代码
    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    struct point{
    	double x,y;
    }p[100010];
    int n,sta[100010],cnt;
    double ans;
    point getvec(point a,point b){return point{(b.x-a.x),(b.y-a.y)};}
    double dis(point a,point b){return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));}
    double xmul(point a,point b){return a.x*b.y-a.y*b.x;}
    bool cmp(point a,point b)
    {	
    		if(a.x==b.x)return a.y<b.y;
    		return a.x<b.x;
    }
    int main()
    {	
    		scanf("%d",&n);
    		for(int i=1;i<=n;i++)
    			scanf("%lf%lf",&p[i].x,&p[i].y);
    		sort(p+1,p+1+n,cmp);
    		sta[++cnt]=1;sta[++cnt]=2;
    		for(int i=3;i<=n;i++)
    		{	
    			point u=getvec(p[sta[cnt-1]],p[sta[cnt]]);
    			point v=getvec(p[sta[cnt]],p[i]);
    			while(xmul(u,v)<0.0)
    			{	
    				if(cnt==1)break;
    				cnt--;
    				u=getvec(p[sta[cnt-1]],p[sta[cnt]]);
    				v=getvec(p[sta[cnt]],p[i]);
    			}
    			sta[++cnt]=i;
    		}
    		int tmp=cnt;
    		sta[++cnt]=n;sta[++cnt]=n-1;
    		for(int i=n-2;i>=1;i--)
    		{	
    			point u=getvec(p[sta[cnt-1]],p[sta[cnt]]);
    			point v=getvec(p[sta[cnt]],p[i]);
    			while(xmul(u,v)<0.0)
    			{	
    				if(cnt==tmp+1)break;
    				cnt--;
    				u=getvec(p[sta[cnt-1]],p[sta[cnt]]);
    				v=getvec(p[sta[cnt]],p[i]);
    			}
    			sta[++cnt]=i;
    		}
    		for(int i=1;i<=cnt-1;i++)
    			ans+=dis(p[sta[i]],p[sta[i+1]]);
    		printf("%.2lf
    ",ans); 
    		return 0;
    }
    
  • 相关阅读:
    Windows Azure Web Site (19) Azure Web App链接到VSTS
    Windows Azure Virtual Machine (35) Azure VM通过Linked DB,执行SQL Job
    Azure PowerShell (16) 并行开关机Azure ARM VM
    Windows Azure Virtual Network (12) 虚拟网络之间点对点连接VNet Peering
    Azure ARM (21) Azure订阅的两种管理模式
    Windows Azure Platform Introduction (14) 申请海外的Windows Azure账户
    Azure ARM (20) 将非托管磁盘虚拟机(Unmanage Disk),迁移成托管磁盘虚拟机(Manage Disk)
    Azure ARM (19) 将传统的ASM VM迁移到ARM VM (2)
    Azure ARM (18) 将传统的ASM VM迁移到ARM VM (1)
    Azure Automation (6) 执行Azure SQL Job
  • 原文地址:https://www.cnblogs.com/Rainy7/p/12250381.html
Copyright © 2011-2022 走看看