zoukankan      html  css  js  c++  java
  • 旋转卡壳与计算几何应用篇

    旋转卡壳

    这种东西,你只要理解做法就行了,因为这种东西十分的玄。

    题目描述
    
    【题意】
    给出n个点的坐标,求最远两点间的距离。
    【输入格式】
    第一行一个整数n(2 ≤ n ≤ 50000)。
    下来n行,每行两个实数x和y表示点坐标(x,y<=|10^6|)。
    【输出格式】
    一行一个实数,表示最远两点间的距离的平方(保留2位小数)。 
    【样例输入】
    5
    0 0
    0 5
    5 0
    5 5
    2 0
    【样例输出】
    50.00
    
    

    凸包

    我们首先可以知道最长的距离肯定在凸包上,如果不在,你可以连线最近的两个在凸包上的点,总有一条是大于这个线段的。

    旋转!跳跃!

    那么,在凸包上我们还能怎么做?O(n^2)

    那么这就很伤了呀。

    但是还是有大佬发明了一个(O(n))的算法。

    我们现在准备两条平行线,卡住这个凸多边形。

    在这里插入图片描述
    来源于https://jvruo.com/archives/79/

    当然,卡住的情况有三种:

    1. 点对点。
    2. 点对边
    3. 边对边

    由于点对点比较难,后面旋转的时候会旋转成点对边,而且边对边可以拆成两个点对边,所以我们只讨论点对边的情况,也就是这种情况:

    在这里插入图片描述

    但是如何求对踵点?选一条边,存在一个点使得构成的三角形面积最大的就是对踵点,同时也就是点对边。

    通过自己感性的理解以及加以画图,应该可以理解为什么是对踵点才是最长的了。

    而且我们会发现,我们逆时针模拟的话,对踵点也是绕着凸包逆时针旋转。

    有人证明过,对踵点对数量小于(frac{3n}{2})

    错误示范

    为什么模拟点对边可以模拟出所有的对踵点。

    举一个简单的例子:

    在这里插入图片描述

    你会发现以(AF,AE)为边的话,(AB)这对对踵点无法找到,这就很气人了,我也以为旋转卡壳是错的。

    但是调试发现(BD)就会以(A)为对踵点了。

    自己画了几个图发现,对踵点至少会被点到一次,最多被点到四次。

    代码

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    #define  N  111000
    using  namespace  std;
    double  eps=1e-10;
    struct dian{double  x,y;}aa[N],sta[N];int  n,top;
    inline  double  mu(dian  x1,dian  x2,dian  y){return  (x1.x-y.x)*(x2.y-y.y)-(x2.x-y.x)*(x1.y-y.y);}//叉积 
    inline  double  dis(dian  x,dian  y){return  (x.x-y.x)*(x.x-y.x)+(x.y-y.y)*(x.y-y.y);}//距离,不用开根,这题开根会掉精 
    inline  bool  cmp(dian  x,dian  y)//排序 
    {
    	double  tt=mu(x,y,aa[1]);
    	if(tt>eps)return  true;
    	else  if(fabs(tt)<=eps  &&  dis(aa[1],x)<dis(aa[1],y))return  true;
    	return  false;
    }
    void  tubao()//凸包 
    {
    	sort(aa+2,aa+n+1,cmp);
    	top=2;sta[1]=aa[1];sta[2]=aa[2];
    	for(int  i=3;i<=n;i++)
    	{
    		while(top>1  &&  mu(sta[top],aa[i],sta[top-1])<=0)top--;
    		sta[++top]=aa[i];
    	}
    }
    inline  double  mymax(double  x,double  y){return  x>y?x:y;}
    void  ka()
    {
    	double  ans=0;
    	sta[++top]=sta[1];/*收尾相连*/int  now=2;//特判点数1与2的情况 
    	for(int  i=1;i<top;i++)
    	{
    		while(mu(sta[i+1],sta[now+1],sta[i])>mu(sta[i+1],sta[now],sta[i]))//叉积求面积,这里需要处理好旋转方向,保证面积大于0 
    		{
    			now++;if(now==top)now=1;
    		}
    		ans=mymax(ans,dis(sta[now],sta[i]));
    	}
    	printf("%.2lf
    ",ans);//dis没开根 
    }
    int  main()
    {
    	scanf("%d",&n);
    	for(int  i=1;i<=n;i++)
    	{
    		scanf("%lf%lf",&aa[i].x,&aa[i].y);
    		if(aa[i].x<aa[1].x  ||  (aa[i].x==aa[1].x  &&  aa[i].y<aa[1].y)){dian  zjj=aa[1];aa[1]=aa[i];aa[i]=zjj;}
    	}
    	tubao();//凸包 
    	ka();//旋转卡壳 
    	return  0;
    }
    

    一些练习

    1

    题目描述
    【题目描述】 
    给定平面上N个点,你需要计算以其中4个点为顶点的正方形的个数。注意这里的正方形边不一定需要和坐标轴平行。 
    【输入文件】 
    第一行一个数N,以下N个点的坐标。 
    【输出文件】 
    一个数正方形的个数。 
    【样例输入】 
    7 
    0 0 
    0 1 
    1 0 
    1 1 
    1 2 
    2 1 
    2 2 
    【样例输出】 
    3 
    【数据规模】 
    对于20%的数据,满足1≤N≤20; 
    对于l00%的数据,满足1≤N≤500,-50≤X[i],Y[i]≤50,点不会重叠。
    
    

    暴力枚举两个点,然后一直暴力一直爽。

    #include<cstdio>
    #include<cstring>
    using  namespace  std;
    int  a[610][2],n,ans;
    bool  bk[610][610];
    void  work(int  x,int  y)
    {
    	int  xx=(a[y][0]-a[x][0]),yy=(a[y][1]-a[x][1]);
    	int  x1=a[x][0]-yy,y1=a[x][1]+xx,x2=a[y][0]-yy,y2=a[y][1]+xx;
    	if(bk[x1][y1]  &&  bk[x2][y2])ans++;
    	x1=a[x][0]+yy,y1=a[x][1]-xx,x2=a[y][0]+yy,y2=a[y][1]-xx;//暴力算出两个三角形,bool矩阵暴力判 
    	if(bk[x1][y1]  &&  bk[x2][y2])ans++;
    }
    int  main()
    {
    	scanf("%d",&n);
    	for(int  i=1;i<=n;i++)
    	{
    		int  x,y;scanf("%d%d",&x,&y);x+=150;y+=150;
    		a[i][0]=x;a[i][1]=y;bk[x][y]=true;
    	}
    	for(int  i=1;i<=n;i++)
    	{
    		for(int  j=i+1;j<=n;j++)work(i,j);
    	}
    	printf("%d
    ",ans/4);//每个矩阵重复算四次 
    	return  0;
    }
    

    2

    题目

    翻译:

    给你一堆构成凸包的点,然后以原点为基准,逆时针输出所有的点。
    
    无三点共线,无其他点在x,y轴。
    

    暴力一直爽,一直暴力一直爽。

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    using  namespace  std;
    double  eps=1e-8;
    struct  dian{double  x,y;}a[100];
    double  pd(dian  x1,dian  x2,dian  y){return  (x1.x-y.x)*(x2.y-y.y)-(x2.x-y.x)*(x1.y-y.y);}
    bool  cmp(dian  x,dian  y)
    {
    	if(pd(x,y,a[1])>eps)return  true;
    	return  false;
    }
    int  n;
    int  main()
    {
    //	freopen("sb.in","r",stdin);
    	n=1;
    	while(scanf("%lf%lf",&a[n].x,&a[n].y)!=EOF)
    	{
    		if(a[n].x<a[1].x  ||  (fabs(a[n].x-a[1].x)<=eps  &&  a[n].y<a[1].y)){dian  z=a[n];a[n]=a[1];a[1]=z;}
    		n++;
    	}
    	n--;
    	sort(a+2,a+n+1,cmp);//类似凸包的排序
    	for(int  i=1;i<=n;i++)
    	{
    		if(a[i].x==0  &&  a[i].y==0)//找到原点
    		{
    			for(int  j=i;j<=n;j++)printf("(%.0lf,%.0lf)
    ",a[j].x,a[j].y);
    			for(int  j=1;j<i;j++)printf("(%.0lf,%.0lf)
    ",a[j].x,a[j].y);
    			break;
    		}
    	}
    	return  0;
    }
    

    3

    3

    大概意思就是说给你一些半径一样的圆,求类似凸包的周长。

    在这里插入图片描述

    那么我们容易看出其实就是凸包的周长加上一个圆周。

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    using  namespace  std;
    double  eps=1e-8,pai=3.1415926535897932;
    struct  dian{double  x,y;}a[1100],sta[1100];int  n,len;double  l;
    inline  double  mu(dian  x,dian  y,dian  z){return  (x.x-z.x)*(y.y-z.y)-(y.x-z.x)*(x.y-z.y);}
    inline  double  dis(dian  x,dian  y){return  sqrt((x.x-y.x)*(x.x-y.x)+(x.y-y.y)*(x.y-y.y));}
    inline  bool  cmp(dian  x,dian  y)
    {
    	double  tt=mu(x,y,a[1]);
    	if(tt>eps)return  true;
    	else  if(fabs(tt)<=eps  &&  dis(x,a[1])-dis(y,a[1])<-eps)return  true;
    	return  false;
    }
    int  main()
    {
    	scanf("%d%lf",&n,&l);
    	for(int  i=1;i<=n;i++)
    	{
    		scanf("%lf%lf",&a[i].x,&a[i].y);
    		if(a[i].x-a[1].x<-eps  ||  (fabs(a[i].x-a[1].x)<=eps  &&  a[i].y-a[1].y<-eps)){dian  z=a[1];a[1]=a[i];a[i]=z;}
    	}
    	sort(a+2,a+n+1,cmp);
    	len=2;sta[1]=a[1];sta[2]=a[2];
    	for(int  i=3;i<=n;i++)
    	{
    		while(len>1  &&  mu(a[i],sta[len],sta[len-1])>=-eps)len--;
    		sta[++len]=a[i];
    	}
    	sta[len+1]=sta[1];
    	double  ans=0.0;
    	for(int  i=1;i<=len;i++)ans+=dis(sta[i],sta[i+1]);
    	ans+=pai*2*l;//圆周
    	printf("%.0lf
    ",ans);
    	return  0;
    }
    

    4

    4

    逆时针输出点,其实很弱智,但是我们可以发现不管作那个点为基准点做逆时针排序,得出的逆时针顺序都一样。

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    using  namespace  std;
    double  eps=1e-8;
    struct  dian{double  x,y;}a[100];
    double  pd(dian  x1,dian  x2,dian  y){return  (x1.x-y.x)*(x2.y-y.y)-(x2.x-y.x)*(x1.y-y.y);}
    bool  cmp(dian  x,dian  y)
    {
    	if(pd(x,y,a[1])>eps)return  true;//无共线情况
    	return  false;
    }
    int  n;
    int  main()
    {
    	n++;
    	while(scanf("%lf%lf",&a[n].x,&a[n].y)!=EOF)
    	{
    		if(fabs(a[n].x)<=eps  &&  fabs(a[n].y)<=eps){dian  z=a[n];a[n]=a[1];a[1]=z;}
    		n++;
    	}
    	n--;
    	sort(a+2,a+n+1,cmp);
    	for(int  i=1;i<=n;i++)printf("(%.0lf,%.0lf)
    ",a[i].x,a[i].y);
    	return  0;
    }
    

    5

    5

    判断是否是稳定凸包。

    也就是每条边都至少有三个点。(特判直线情况)

    用类似凸包的方法排序(虽然不是很需要,可以把凸包去掉,并改改代码,但是懒得改了,当时),但是应当特判最后一条边。

    一个数据:

    1
    7
    0 0
    1 0
    2 0
    2 1
    2 2
    1 2
    0 2
    
    NO
    
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cmath>
    #include<algorithm>
    using  namespace  std;
    struct  dian
    {
    	double  x,y;
    }a[1100],sta[1100];int  n;double  eps=1e-7;
    double  mu(dian  x,dian  y,dian  z){return  (x.x-z.x)*(y.y-z.y)-(y.x-z.x)*(x.y-z.y);}
    double  dis(dian  x,dian  y){return  sqrt((x.x-y.x)*(x.x-y.x)+(x.y-y.y)*(x.y-y.y));}
    bool  cmp(dian  x,dian  y)
    {
    	double  tt=mu(x,y,a[1]);
    	if(tt>eps)return  true;
    	else  if(fabs(tt)<=eps  &&  dis(a[1],x)-dis(a[1],y)<=eps)return  true;
    	return  false;
    }
    int  main()
    {
    	int  T;scanf("%d",&T);
    	while(T--)
    	{
    		scanf("%d",&n);
    		for(int  i=1;i<=n;i++)
    		{
    			scanf("%lf%lf",&a[i].x,&a[i].y);
    			if(a[i].x-a[1].x<-eps  ||  (fabs(a[i].x-a[1].x)<=eps  &&  a[i].y-a[1].y<-eps)){dian  z=a[1];a[1]=a[i];a[i]=z;}
    		}
    		sort(a+2,a+n+1,cmp);
    		int  top=2;sta[1]=a[1];sta[2]=a[2];
    		for(int  i=3;i<=n;i++)
    		{
    			while(top>1  &&  mu(a[i],sta[top],sta[top-1])>eps)top--;
    			sta[++top]=a[i];
    		}
    		//凸包判排序
    		if((n-1)>1  &&  fabs(mu(a[n-1],sta[top],sta[1]))<=eps)//最后一条边有点与top共线 
    		{
    			dian  now=sta[top];
    			for(int  i=top;i>1;i--)
    			{
    				if(fabs(mu(sta[i],now,sta[1]))<=eps)top--;
    				else  break;
    			}//去掉最后一条边,重复点的情况。 
    			sta[++top]=now;//保留一个点。 
    			if(top<=2)//一条直线 
    			{
    				printf("NO
    ");
    				continue;
    			}
    			bool  bk=false;
    			for(int  i=3;i<=top;i++)
    			{
    				if(fabs(mu(sta[i-2],sta[i-1],sta[i]))<=eps)bk=true;
    				else
    				{
    					if(bk==true)bk=false; 
    					else  break;
    				}
    			}
    			if(bk==false)printf("NO
    ");
    			else printf("YES
    ");
    		}
    		else  printf("NO
    ");
    	}
    	return  0;
    }
    

    6

    6

    判断情况。

    分别用斜率与两点式((ax+by+c=0),求的公式在代码中有)做了。

    斜率版:

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cmath>
    using  namespace  std;
    struct  dian
    {
    	double  x,y;
    	dian(double  xx=0.0,double  yy=0.0){x=xx;y=yy;}
    }xx1,yy1,xx2,yy2;double  eps=1e-7;
    dian  pd(dian  x,dian  y)
    {
    	dian  z;z.x=(x.y-y.y)/(x.x-y.x);//斜率
    	z.y=x.y-x.x*z.x;
    	return  z;
    }
    double  mu(dian  x,dian  y,dian  z){return  (x.x-z.x)*(y.y-z.y)-(y.x-z.x)*(x.y-z.y);}
    dian  jd()
    {
    	double  t1=mu(yy2,xx1,xx2),t2=mu(yy2,yy1,xx2);
    	return  dian((t1*yy1.x-t2*xx1.x)/(t1-t2),(t1*yy1.y-t2*xx1.y)/(t1-t2));
    }
    void  solve()
    {
    	dian  q=pd(xx1,yy1),p=pd(xx2,yy2);
    	if(xx1.x==yy1.x)//特判y=0的直线
    	{
    		if(xx2.x==yy2.x)
    		{
    			if(xx1.x==xx2.x)printf("LINE
    ");
    			else  printf("NONE
    ");
    		}
    		else  printf("POINT %.2lf %.2lf
    ",xx1.x,xx1.x*p.x+p.y);
    		return  ;
    	}
    	if(fabs(q.x-p.x)<=eps)
    	{
    		if(fabs(q.y-p.y)<=eps)printf("LINE
    ");
    		else  printf("NONE
    ");
    	}
    	else
    	{
    		dian  z=jd();
    		printf("POINT %.2lf %.2lf
    ",z.x,z.y);
    	}
    }
    int  main()
    {
    //	freopen("sb.out","w",stdout);
    	printf("INTERSECTING LINES OUTPUT
    ");
    	int  n;scanf("%d",&n);
    	for(int  i=1;i<=n;i++)
    	{
    		scanf("%lf%lf%lf%lf%lf%lf%lf%lf",&xx1.x,&xx1.y,&yy1.x,&yy1.y,&xx2.x,&xx2.y,&yy2.x,&yy2.y);
    		solve();
    	}
    	printf("END OF OUTPUT
    ");
    	return  0;
    }
    

    两点式:

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cmath>
    using  namespace  std;
    struct  dian
    {
    	double  x,y;
    	dian(double  xx=0.0,double  yy=0.0){x=xx;y=yy;}
    }xx1,yy1,xx2,yy2;double  eps=1e-6;
    struct  node{double  x,y,z;};
    node  pd(dian  x,dian  y)//求两点式 
    {
    	//(x-x1)/(x2-x1)=(y-y1)/(y2-y1)相似三角形可以求出此式子
    	//(x-x1)(y2-y1)=(y-y1)(x2-x1)相似三角形 
    	//(y2-y1)x+(x1-x2)y+(x1y1-x1y2+y1x2-x1y1)
    	//(y2-y1)x+(x1-x2)y+(y1x2-x1y2)
    	node  z;z.x=(y.y-x.y);z.y=(x.x-y.x);z.z=(x.y*y.x-x.x*y.y);
    	return  z;
    }
    double  mu(dian  x,dian  y,dian  z){return  (x.x-z.x)*(y.y-z.y)-(y.x-z.x)*(x.y-z.y);}
    dian  jd()
    {
    	double  t1=mu(yy2,xx1,xx2),t2=mu(yy2,yy1,xx2);
    	return  dian((t1*yy1.x-t2*xx1.x)/(t1-t2),(t1*yy1.y-t2*xx1.y)/(t1-t2));
    }
    void  solve()
    {
    	node  q=pd(xx1,yy1),p=pd(xx2,yy2);
    	if(fabs(q.x*p.y-q.y*p.x)<=eps)
    	{
    		if(fabs(q.z*p.y-p.z*q.y)<=eps  &&  fabs(q.z*p.x-p.z*q.x)<=eps)printf("LINE
    ");
    		else  printf("NONE
    ");
    	}
    	else
    	{
    		dian  z=jd();
    		printf("POINT %.2lf %.2lf
    ",z.x,z.y);
    	}
    }
    int  main()
    {
    //	freopen("sb.out","w",stdout);
    	printf("INTERSECTING LINES OUTPUT
    ");
    	int  n;scanf("%d",&n);
    	for(int  i=1;i<=n;i++)
    	{
    		scanf("%lf%lf%lf%lf%lf%lf%lf%lf",&xx1.x,&xx1.y,&yy1.x,&yy1.y,&xx2.x,&xx2.y,&yy2.x,&yy2.y);
    		solve();
    	}
    	printf("END OF OUTPUT
    ");
    	return  0;
    }
    

    7

    7

    //双倍经验
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cmath>
    using  namespace  std;
    double  eps=1e-8;
    struct  dian{double  x,y;};
    struct  line{dian  x,y;}a[110000];
    double  mu(dian  x,dian  y,dian  z){return  (x.x-z.x)*(y.y-z.y)-(y.x-z.x)*(x.y-z.y);}
    double  mymin(double  x,double  y){return  x<y?x:y;}
    double  mymax(double  x,double  y){return  x>y?x:y;}
    bool  pd(line  x,dian  y){return  (y.x>=mymin(x.x.x,x.y.x)  &&  y.x<=mymax(x.x.x,x.y.x)  &&  y.y>=mymin(x.x.y,x.y.y)  &&  y.y<=mymax(x.x.y,x.y.y));}
    bool  solve(line  x,line  y)
    {
    	if(mu(y.y,x.x,y.x)*mu(y.y,x.y,y.x)<-eps  &&  mu(x.y,y.x,x.x)*mu(x.y,y.y,x.x)<-eps)return  true;
    	if((fabs(mu(x.x,y.x,y.y))<=eps  &&  pd(y,x.x)==true)  ||  
    	(fabs(mu(x.y,y.x,y.y))<=eps  &&  pd(y,x.y)==true)  ||  
    	(fabs(mu(y.x,x.x,x.y))<=eps  &&  pd(x,y.x)==true)  ||  
    	(fabs(mu(y.y,x.x,x.y))<=eps  &&  pd(x,y.y)==true))return  true;
    	return  false;
    }
    int  n,ans[110000];
    int  main()
    {
    	while(scanf("%d",&n)!=EOF)
    	{
    		if(n==0)break;
    		for(int  i=1;i<=n;i++)scanf("%lf%lf%lf%lf",&a[i].x.x,&a[i].x.y,&a[i].y.x,&a[i].y.y);
    		ans[0]=0;
    		for(int  i=1;i<=n;i++)
    		{
    			bool  bk=false;
    			for(int  j=i+1;j<=n;j++)
    			{
    				if(solve(a[i],a[j])==true){bk=true;break;}
    			}
    			if(bk==false)ans[++ans[0]]=i;
    		}
    		printf("Top sticks:");
    		for(int  i=1;i<ans[0];i++)printf(" %d,",ans[i]);
    		printf(" %d.
    ",ans[ans[0]]);
    	}
    	return  0;
    }
    

    8

    8

    逆时针的半平面交双倍经验。

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cmath>
    #include<algorithm>
    #define  N  110
    using  namespace  std;
    double  eps=1e-8;
    int  n;
    struct  dian
    {
    	double  x,y;
    	dian(double  xx=0,double  yy=0){x=xx;y=yy;}
    };
    struct  line
    {
    	dian  x,y;
    	double  agr;
    	void  made(){agr=atan2(y.y-x.y,y.x-x.x);}
    }a[N],sta[N];int  head,tail;
    inline  double  mu(dian  x,dian  y,dian  z){return  (x.x-z.x)*(y.y-z.y)-(y.x-z.x)*(x.y-z.y);}
    inline  dian  jd(line  x,line  y)
    {
    	double  t1=mu(y.x,x.x,y.y),t2=mu(y.x,x.y,y.y);
    	return  dian((t1*x.y.x-t2*x.x.x)/(t1-t2),(t1*x.y.y-t2*x.x.y)/(t1-t2));
    }
    inline  bool  safe(line  x,dian  y){return  mu(y,x.y,x.x)<=eps;}
    inline  bool  cmp(line  x,line  y)
    {
    	if(x.agr-y.agr<-eps)return  true;
    	else  if(fabs(x.agr-y.agr)<=eps  &&  safe(y,x.x)==true)return  true;
    	return  false;
    }
    inline  bool  bpm()
    {
    	sort(a+1,a+n+1,cmp);
    	int  tp=1;for(int  i=2;i<=n;i++)if(fabs(a[i].agr-a[i-1].agr)>eps)a[++tp]=a[i];
    	n=tp;
    	head=1;tail=2;sta[1]=a[1];sta[2]=a[2];
    	for(int  i=3;i<=n;i++)
    	{
    		while(head<tail  &&  safe(a[i],jd(sta[tail],sta[tail-1]))==false)tail--;
    		while(head<tail  &&  safe(a[i],jd(sta[head],sta[head+1]))==false)head++;
    		sta[++tail]=a[i];
    	}
    	while(head<tail  &&  safe(sta[head],jd(sta[tail],sta[tail-1]))==false)tail--;
    	if(tail-head+1<=2)return  false;
    	return  true;
    }
    int  main()
    {
    	while(scanf("%d",&n)!=EOF)
    	{
    		if(n==0)break;
    		for(int  i=1;i<=n;i++)
    		{
    			double  x,y;scanf("%lf%lf",&x,&y);
    			a[i-1].y.x=a[i].x.x=x;a[i-1].y.y=a[i].x.y=y;
    			a[i-1].made();
    		}
    		a[n].y.x=a[1].x.x;a[n].y.y=a[1].x.y;a[n].made();
    		if(bpm())printf("1
    ");
    		else  printf("0
    ");
    	}
    	return  0;
    }
    

    9

    9

    双倍经验,变成顺时针。

    //非凸多边形不能用叉积判顺逆 
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cmath>
    #include<algorithm>
    #define  N  210
    using  namespace  std;
    double  eps=1e-8;
    int  n;
    struct  dian
    {
    	double  x,y;
    	dian(double  xx=0,double  yy=0){x=xx;y=yy;}
    };
    struct  line
    {
    	dian  x,y;
    	double  agr;
    	void  made(){agr=atan2(y.y-x.y,y.x-x.x);}
    }a[N],sta[N];int  head,tail;
    inline  double  mu(dian  x,dian  y,dian  z){return  (x.x-z.x)*(y.y-z.y)-(y.x-z.x)*(x.y-z.y);}
    inline  dian  jd(line  x,line  y)
    {
    	double  t1=mu(y.x,x.x,y.y),t2=mu(y.x,x.y,y.y);
    	return  dian((t1*x.y.x-t2*x.x.x)/(t1-t2),(t1*x.y.y-t2*x.x.y)/(t1-t2));
    }
    inline  bool  safe(line  x,dian  y){return  mu(y,x.y,x.x)<=eps;}
    inline  bool  cmp(line  x,line  y)
    {
    	if(x.agr-y.agr<-eps)return  true;
    	else  if(fabs(x.agr-y.agr)<=eps  &&  safe(y,x.x)==true)return  true;
    	return  false;
    }
    inline  bool  bpm()
    {
    	sort(a+1,a+n+1,cmp);
    	int  tp=1;for(int  i=2;i<=n;i++)if(fabs(a[i].agr-a[i-1].agr)>eps)a[++tp]=a[i];
    	n=tp;
    	head=1;tail=2;sta[1]=a[1];sta[2]=a[2];
    	for(int  i=3;i<=n;i++)
    	{
    		while(head<tail  &&  safe(a[i],jd(sta[tail],sta[tail-1]))==false)tail--;
    		while(head<tail  &&  safe(a[i],jd(sta[head],sta[head+1]))==false)head++;
    		sta[++tail]=a[i];
    	}
    	while(head<tail  &&  safe(sta[head],jd(sta[tail],sta[tail-1]))==false)tail--;
    	if(tail-head+1<=2)return  false;
    	return  true;
    }
    int  main()
    {
    	int  T;scanf("%d",&T);
    	for(int  i=1;i<=T;i++)
    	{
    		scanf("%d",&n);
    		for(int  i=1;i<=n;i++)scanf("%lf%lf",&a[i].x.x,&a[i].x.y);
    		for(int  i=n-1;i>=1;i--)a[i+1].y=a[i].x,a[i+1].made();
    		a[1].y=a[n].x;a[1].made();
    		if(bpm())printf("YES
    ");
    		else  printf("NO
    ");
    	}
    	return  0;
    }
    

    10

    10

    多次双倍经验,顺时针。

    //非凸多边形不能用叉积判顺逆 
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cmath>
    #include<algorithm>
    #define  N  210
    using  namespace  std;
    double  eps=1e-8;
    int  n;
    struct  dian
    {
    	double  x,y;
    	dian(double  xx=0,double  yy=0){x=xx;y=yy;}
    };
    struct  line
    {
    	dian  x,y;
    	double  agr;
    	void  made(){agr=atan2(y.y-x.y,y.x-x.x);}
    }a[N],sta[N];int  head,tail;
    inline  double  mu(dian  x,dian  y,dian  z){return  (x.x-z.x)*(y.y-z.y)-(y.x-z.x)*(x.y-z.y);}
    inline  dian  jd(line  x,line  y)
    {
    	double  t1=mu(y.x,x.x,y.y),t2=mu(y.x,x.y,y.y);
    	return  dian((t1*x.y.x-t2*x.x.x)/(t1-t2),(t1*x.y.y-t2*x.x.y)/(t1-t2));
    }
    inline  bool  safe(line  x,dian  y){return  mu(y,x.y,x.x)<=eps;}
    inline  bool  cmp(line  x,line  y)
    {
    	if(x.agr-y.agr<-eps)return  true;
    	else  if(fabs(x.agr-y.agr)<=eps  &&  safe(y,x.x)==true)return  true;
    	return  false;
    }
    inline  bool  bpm()
    {
    	sort(a+1,a+n+1,cmp);
    	int  tp=1;for(int  i=2;i<=n;i++)if(fabs(a[i].agr-a[i-1].agr)>eps)a[++tp]=a[i];
    	n=tp;
    	head=1;tail=2;sta[1]=a[1];sta[2]=a[2];
    	for(int  i=3;i<=n;i++)
    	{
    		while(head<tail  &&  safe(a[i],jd(sta[tail],sta[tail-1]))==false)tail--;
    		while(head<tail  &&  safe(a[i],jd(sta[head],sta[head+1]))==false)head++;
    		sta[++tail]=a[i];
    	}
    	while(head<tail  &&  safe(sta[head],jd(sta[tail],sta[tail-1]))==false)tail--;
    	if(tail-head+1<=2)return  false;
    	return  true;
    }
    int  main()
    {
    	int  T=0;
    	while(scanf("%d",&n)!=EOF)
    	{
    		if(n==0)break;
    		T++;printf("Floor #%d
    ",T);
    		for(int  i=1;i<=n;i++)scanf("%lf%lf",&a[i].x.x,&a[i].x.y);
    		for(int  i=n-1;i>=1;i--)a[i+1].y=a[i].x,a[i+1].made();
    		a[1].y=a[n].x;a[1].made();
    		if(bpm())printf("Surveillance is possible.
    
    ");
    		else  printf("Surveillance is impossible.
    
    ");
    	}
    	return  0;
    }
    

    11

    11

    顺时针,在多求个面积。。。

    //非凸多边形不能用叉积判顺逆 
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cmath>
    #include<algorithm>
    #define  N  2110
    using  namespace  std;
    double  eps=1e-8;
    int  n;
    struct  dian
    {
    	double  x,y;
    	dian(double  xx=0,double  yy=0){x=xx;y=yy;}
    }pl[N];
    struct  line
    {
    	dian  x,y;
    	double  agr;
    	void  made(){agr=atan2(y.y-x.y,y.x-x.x);}
    }a[N],sta[N];int  head,tail;
    inline  double  mu(dian  x,dian  y,dian  z){return  (x.x-z.x)*(y.y-z.y)-(y.x-z.x)*(x.y-z.y);}
    inline  dian  jd(line  x,line  y)
    {
    	double  t1=mu(y.x,x.x,y.y),t2=mu(y.x,x.y,y.y);
    	return  dian((t1*x.y.x-t2*x.x.x)/(t1-t2),(t1*x.y.y-t2*x.x.y)/(t1-t2));
    }
    inline  bool  safe(line  x,dian  y){return  mu(y,x.y,x.x)<=eps;}
    inline  bool  cmp(line  x,line  y)
    {
    	if(x.agr-y.agr<-eps)return  true;
    	else  if(fabs(x.agr-y.agr)<=eps  &&  safe(y,x.x)==true)return  true;
    	return  false;
    }
    inline  void  bpm()
    {
    	sort(a+1,a+n+1,cmp);
    	int  tp=1;for(int  i=2;i<=n;i++)if(fabs(a[i].agr-a[i-1].agr)>eps)a[++tp]=a[i];
    	n=tp;
    	head=1;tail=2;sta[1]=a[1];sta[2]=a[2];
    	for(int  i=3;i<=n;i++)
    	{
    		while(head<tail  &&  safe(a[i],jd(sta[tail],sta[tail-1]))==false)tail--;
    		while(head<tail  &&  safe(a[i],jd(sta[head],sta[head+1]))==false)head++;
    		sta[++tail]=a[i];
    	}
    	while(head<tail  &&  safe(sta[head],jd(sta[tail],sta[tail-1]))==false)tail--;
    	if(tail-head+1<=2)
    	{
    		printf("0.00
    ");
    		return  ;
    	}
    	double  ans=0;int  len=0;
    	for(int  i=head;i<tail;i++)pl[++len]=jd(sta[i],sta[i+1]);pl[++len]=jd(sta[head],sta[tail]);
    	for(int  i=3;i<=len;i++)ans+=mu(pl[i-1],pl[i],pl[1]);
    	printf("%.2lf
    ",fabs(ans/2));
    }
    int  main()
    {
    	int  T;scanf("%d",&T);
    	for(int  i=1;i<=T;i++)
    	{
    		scanf("%d",&n);
    		for(int  i=1;i<=n;i++)scanf("%lf%lf",&a[i].x.x,&a[i].x.y);
    		for(int  i=n-1;i>=1;i--)a[i+1].y=a[i].x,a[i+1].made();
    		a[1].y=a[n].x;a[1].made();
    		bpm();
    	}
    	return  0;
    }
    
  • 相关阅读:
    异常:调用链顺序
    日志分割建议
    java异常处理规范
    忽略字母大小写情况下统计字符出现的次数
    十六进制转化为十进制
    搜索(剪枝优化):HDU 5113 Black And White
    水题:HDU 5112 A Curious Matt
    综合(奇技淫巧):HDU 5118 GRE Words Once More!
    网络流(费用流)CodeForces 321B:Ciel and Duel
    数学(概率)CodeForces 626D:Jerry's Protest
  • 原文地址:https://www.cnblogs.com/zhangjianjunab/p/10922770.html
Copyright © 2011-2022 走看看