cogs1600 奶牛冰壶 计算几何

链接:http://cogs.pro/cogs/problem/problem.php?pid=1600

题意:两方打冰壶,分别计算出双方用三个以上球夹住的对方球的数量。

首先我们要明确一点:这个问题可以转化为求出凸包内包含的点的数量。理由很显然:如果你用三个不在凸包外延的点夹住了某颗石头,比它们范围更大的凸包一定也可以夹住它。因此问题转化为求出凸包内包含的另一方点的数量。首先,凸包可以在$O(nlogn)$时间内求出,因此问题就转化为如何判断点是否在凸包内。

一种判断点在多边形之内的方法是射线法,即由该点向任意方向射出一条射线,判断与多边形交点数目,如果是奇数次相交证明在多边形内部。但这样会带来一个问题:如果射线与图形非规范相交需要再引出射线,而如何判断是否规范相交又是个大问题,这样会引起码量急剧上升,时间效率却不太好。

为此,这里介绍一种更快、难度更小、范围更广(甚至自交的多边形也可以)的判断方法:转角法。基本思想就是看多边形转一圈是相对这个点转了多少,$360°$为在图形外,$0°$为在图形内,$180°$为在图形上。但是如果直接计算,这样会涉及大量反三角函数,效率低还是其次,更大的问题是精度爆炸成GTX690。因此,我们实际上的操作是假想这个点拉出一条向右的射线,正着过次数+1,反着过次数-1,最后只要不是0,点就在多边形里面了。具体实现可以参考代码。

问题的解法很明显了。首先分别求出双方冰壶组成的凸包,随后对于每一个点,判断是否被对方凸包覆盖。可能有人会担心超时,因为最坏情况下检查是$O(n^2)$的,但实际上算法效果非常好,不但没有超时还上了榜。

本题最大的坑点在于:边界线计入图形内部……

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 #include<vector>
  6 #include<cmath>
  7 using namespace std;
  8 const int maxn=50005;
  9 const double eps=1e-8;
 10 int n;
 11 struct point
 12 {
 13     double x,y;
 14     bool operator<(const point &b)const 
 15     {
 16         return x==b.x?y<b.y:x<b.x;
 17     }
 18     friend point operator -(point a,point b)
 19     {
 20         return (point){a.x-b.x,a.y-b.y};
 21     }
 22 }pa[maxn],pb[maxn];
 23 vector<point>convexa,convexb;
 24 double dis(point a,point b)
 25 {
 26     return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
 27 }
 28 double cross(point a,point b)
 29 {
 30     return a.x*b.y-b.x*a.y;
 31 }
 32 double across(point a,point b,point c)
 33 {
 34     return cross(b-a,c-a)>eps;
 35 }
 36 int s[maxn],vis[maxn];
 37 void graham()
 38 {
 39     memset(vis,0,sizeof(vis));
 40     sort(pa+1,pa+n+1);
 41     s[0]=0;s[++s[0]]=1;s[++s[0]]=2;
 42     for(int i=3;i<=n;i++)
 43     {
 44         while(s[0]>1&&!across(pa[s[s[0]]],pa[i],pa[s[s[0]-1]]))s[0]--;
 45         s[++s[0]]=i;
 46     }
 47     for(int i=1;i<=s[0];i++)vis[s[i]]=1,convexa.push_back(pa[s[i]]);
 48     s[0]=0;s[++s[0]]=n;s[++s[0]]=n-1;
 49     for(int i=n-2;i;i--)
 50     {
 51         while(s[0]>1&&across(pa[s[s[0]]],pa[s[s[0]-1]],pa[i]))s[0]--;
 52         s[++s[0]]=i;
 53     }
 54     for(int i=1;i<=s[0];i++)
 55         if(!vis[s[i]])convexa.push_back(pa[s[i]]);
 56     memset(vis,0,sizeof(vis));
 57     sort(pb+1,pb+n+1);
 58     s[0]=0;s[++s[0]]=1;s[++s[0]]=2;
 59     for(int i=3;i<=n;i++)
 60     {
 61         while(s[0]>1&&!across(pb[s[s[0]]],pb[i],pb[s[s[0]-1]]))s[0]--;
 62         s[++s[0]]=i;
 63     }
 64     for(int i=1;i<=s[0];i++)vis[s[i]]=1,convexb.push_back(pb[s[i]]);
 65     s[0]=0;s[++s[0]]=n;s[++s[0]]=n-1;
 66     for(int i=n-2;i;i--)
 67     {
 68         while(s[0]>1&&across(pb[s[s[0]]],pb[s[s[0]-1]],pb[i]))s[0]--;
 69         s[++s[0]]=i;
 70     }
 71     for(int i=1;i<=s[0];i++)
 72         if(!vis[s[i]])convexb.push_back(pb[s[i]]);
 73 }
 74 int dcmp(double x)
 75 {
 76     if(fabs(x)<eps)return 0;
 77     return x<0?-1:1;
 78 }
 79 double dot(point a,point b)
 80 {
 81     return a.x*b.x+a.y*b.y;
 82 }
 83 bool onsegment(point p,point a1,point a2)
 84 {
 85     return dcmp(cross(a1-p,a2-p))==0&&dcmp(dot(a1-p,a2-p))<0;
 86 }
 87 int ispointinpolygon(point p,vector<point>tmp)
 88 {
 89     int wn=0,t=tmp.size();
 90     for(int i=0;i<t;i++)
 91     {
 92         if(onsegment(p,tmp[i],tmp[(i+1)%t]))return 1;
 93         int k=dcmp(cross(tmp[(i+1)%t]-tmp[i],p-tmp[i]));
 94         int d1=dcmp(tmp[i].y-p.y),d2=dcmp(tmp[(i+1)%t].y-p.y);
 95         if(k>0&&d1<=0&&d2>0)wn++;
 96         if(k<0&&d2<=0&&d1>0)wn--;
 97     }
 98     return wn!=0;
 99 }
100 int haha()
101 {
102     freopen("curling.in","r",stdin);
103     freopen("curling.out","w",stdout);
104     scanf("%d",&n);
105     for(int i=1;i<=n;i++)scanf("%lf%lf",&pa[i].x,&pa[i].y);
106     for(int i=1;i<=n;i++)scanf("%lf%lf",&pb[i].x,&pb[i].y);
107     graham();
108     int ans1=0,ans2=0;
109     for(int i=1;i<=n;i++)ans2+=ispointinpolygon(pa[i],convexb),ans1+=ispointinpolygon(pb[i],convexa);
110     printf("%d %d\n",ans1,ans2);
111 }
112 int sb=haha();
113 int main(){;}
cogs1600

 

posted @ 2017-08-05 21:09  ccc000111  阅读(319)  评论(1编辑  收藏  举报