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(){;}
只要是活着的东西,就算是神我也杀给你看。