CodeChef---- February Challenge 2018----Points Inside A Polygon
链接:https://www.codechef.com/FEB18/problems/POINPOLY
You have to find any ⌊N/10⌋ distinct points with integer coordinates that lie strictly insidethe polygon, or determine that such a set of points doesn't exist.
Note: ⌊⌋ denotes the floor function, typically used in integer division.
Input
- The first line of the input contains a single integer T denoting the number of test cases. The description of T test cases follows.
- The first line of each test case contains a single integer N denoting the number of vertices of the polygon.
- The following N lines describe the vertices of the polygon in anticlockwise order. Each of these lines contains two space-separated integers x and y denoting the coordinates of one vertex.
Output
For each test case, if a valid set of points doesn't exist, print a single line containing the integer -1. Otherwise, print ⌊N/10⌋ lines. Each of these lines should contain two space-separated integers denoting the coordinates of one point.
The coordinates of all points should be integers with absolute value not exceeding 109. If there are multiple solutions, you may print any one.
Constraints
- 1 ≤ T ≤ 105
- 10 ≤ N ≤ 105
- sum of N over all test cases ≤ 5 · 105
- |x|, |y| ≤ 109
- no three vertices of the polygon will be collinear
Subtasks
Subtask #1 (30 points): 1 ≤ T ≤ 100, 10 ≤ N ≤ 100
Subtask #2 (70 points): original constraints
Example
Input 1 11 0 0 1 1 2 3 2 5 0 10 -2 10 -5 9 -8 7 -8 4 -6 1 -2 0 Output 0 1
////////////////////////////////////////////////////////////
看到这道题是一脸懵逼的,后来想着在每个端点附近是有四个点,至少有一个点在凸包内,最多每三个点共享一个凸包内相邻点,于是肯定可以做
那么对于一个点,怎么判断是否在凸包内呢,我是把每个端点求出跟左右两点的直线方程式(o1求)然后先对于每一个端点,对于凸包的端点,判断是在每一条线的那一侧(根据方程式用正负值来表示)
(但是不可能n²来求得对每个点来说,凸包端点是在线的哪一侧,所以我去了最上下左右四个点,用于判断,只要四个点是不同的,肯定至少有一个点跟判定点不共线,就可以求得是在哪一侧,后来发现出bug了
就是当最上下左右四个点指向的是两个点的时候,代码是有bug的,错误的例子就是左下右上处在最上下左右,这时候需要特判一下,如果出现了,将相同的值换成另外一个其他点就行了)
然后对于某个端点相邻的点,求出他在两条线的哪一侧,如果跟凸包端点都相同,那就是在凸包内了,后来想着,几十个点围成一个宽为1的正方形不就gg了,所以还加了对于上下左右四个点是否也符合条件
(后来问了zk的做法之后,发现,原来凸包是没有三点共线的吗,,,emmm我好菜)(前面会出现那个地方会出bug也是因为加了上下左右点的判定,但是这四个点集中在两个点的话,如果两点相邻,那么他们的方向值就是 0 0,这样
任何点都会被判定成不符合)
然后贴个自己的代码
///////////////////////////////////////////////////////////////////////////
1 #include <bits/stdc++.h> 2 #define mst(a,b) memset((a),(b), sizeof a) 3 #define lowbit(a) ((a)&(-a)) 4 #define IOS ios::sync_with_stdio(0);cin.tie(0); 5 using namespace std; 6 typedef long long ll; 7 const int mod=1e9+7; 8 const int maxn=1e5+10; 9 int n; 10 struct point{int x,y;}; 11 struct line{ll a,b,c;}; 12 point po[maxn]; 13 line li[maxn][2]; 14 int cc[maxn][2]; 15 int dx[]={0,0,-1,1},dy[]={1,-1,0,0}; 16 vector<pair<int,int> >ans; 17 int up=1e5+2,down=1e5+3,le=1e5+4,ri=1e5+5; 18 void gtline(int i,int j,line& k){ 19 k.a=po[i].y-po[j].y; 20 k.b=po[j].x-po[i].x; 21 k.c=(ll)po[i].x*po[j].y-(ll)po[j].x*po[i].y; 22 } 23 24 int dir(line&k,int x,int y){ 25 ll g=k.a*x+k.b*y+k.c; 26 if(g==0)return 0; 27 if(g<0)return -1; 28 return 1; 29 } 30 bool gg(int p,int x,int y){ 31 if(dir(li[p][0],x,y)!=cc[p][0])return true; 32 if(dir(li[p][1],x,y)!=cc[p][1])return true; 33 return false; 34 } 35 bool ok(int p,int x,int y){ 36 if(gg(p,x,y))return false; 37 if(gg(le,x,y))return false; 38 if(gg(ri,x,y))return false; 39 if(gg(up,x,y))return false; 40 if(gg(down,x,y))return false; 41 return true; 42 } 43 void co(){ 44 int ne=n/10; 45 set<pair<int,int> >use; 46 vector<int>kk; 47 if(ans.size()<ne){printf("-1\n");return;} 48 for(int i=0;i<ans.size();++i){ 49 if(!use.count(ans[i])){ 50 use.insert(ans[i]); 51 kk.push_back(i); 52 --ne; 53 if(!ne)break; 54 } 55 } 56 if(ne){printf("-1\n");return;} 57 for(int i=0;i<kk.size();++i){ 58 printf("%d %d\n",ans[kk[i]].first,ans[kk[i]].second); 59 } 60 61 } 62 void check(){ 63 if(up==ri||up==le){ 64 int tmp=1e5+2; 65 for(int i=0;i<n;++i)if(i!=ri&&i!=le){ 66 if(po[i].y>po[tmp].y)tmp=i; 67 } 68 up=tmp; 69 } 70 if(down==ri||down==le){ 71 int tmp=1e5+3; 72 for(int i=0;i<n;++i)if(i!=ri&&i!=le){ 73 if(po[i].y<po[tmp].y)tmp=i; 74 } 75 down=tmp; 76 } 77 } 78 int main(){ 79 #ifdef local 80 freopen("in.txt","r",stdin); 81 //freopen("out.txt","w",stdout); 82 #endif 83 int t;scanf("%d",&t); 84 po[up].y=-1e9-10;po[down].y=1e9+10;po[le].x=1e9+10;po[ri].x=-1e9-10; 85 while(t--){ 86 up=1e5+2,down=1e5+3,le=1e5+4,ri=1e5+5; 87 scanf("%d",&n); 88 for(int i=0;i<n;++i){ 89 scanf("%d%d",&po[i].x,&po[i].y); 90 if(po[i].x<po[le].x)le=i; 91 if(po[i].x>po[ri].x)ri=i; 92 if(po[i].y>po[up].y)up=i; 93 if(po[i].y<po[down].y)down=i; 94 } 95 check(); 96 for(int i=0;i<n;++i){ 97 for(int j=0;j<2;++j){ 98 int ad=(j==0?-1:1); 99 gtline(i,(i+ad+n)%n,li[i][j]);cc[i][j]=0; 100 cc[i][j]=dir(li[i][j],po[le].x,po[le].y); 101 if(!cc[i][j]) 102 cc[i][j]=dir(li[i][j],po[ri].x,po[ri].y); 103 if(!cc[i][j]) 104 cc[i][j]=dir(li[i][j],po[up].x,po[up].y); 105 if(!cc[i][j]) 106 cc[i][j]=dir(li[i][j],po[down].x,po[down].y); 107 } 108 } 109 ans.clear(); 110 for(int i=0;i<n;++i){ 111 for(int j=0;j<4;++j){ 112 if(ok(i,po[i].x+dx[j],po[i].y+dy[j])){ 113 ans.push_back(make_pair(po[i].x+dx[j],po[i].y+dy[j])); 114 break; 115 } 116 } 117 } 118 co(); 119 } 120 return 0; 121 }
zk 的做法是根据端点坐标的奇偶性分成四类,这样数量最多的一类至少有n/4个点(鸽笼原理),然后因为三点不共线,由于凸包每两个端点的中点一定在凸包内,或是在端点连线上(两点相邻)
由于三点不共线,所以,做多那个类以至少可以产出n/4-2个端点,这个极限情况只有当n==10可能出现,实际能够产出点应该是(n/4)²级别的(这是我猜的)
然后放一下根据zk的解法写的代码(瞬间短了很多)
//////////////////////////////////////////////////////////////
1 #include <bits/stdc++.h> 2 #define mst(a,b) memset((a),(b), sizeof a) 3 #define lowbit(a) ((a)&(-a)) 4 #define IOS ios::sync_with_stdio(0);cin.tie(0); 5 using namespace std; 6 typedef long long ll; 7 const int mod=1e9+7; 8 const int maxn=1e5+10; 9 int x[maxn],y[maxn]; 10 vector<int>has[2][2]; 11 vector<pair<int,int> >ans; 12 set<pair<int,int> >uu; 13 int main(){ 14 #ifdef local 15 freopen("in.txt","r",stdin); 16 //freopen("out.txt","w",stdout); 17 #endif 18 int t;scanf("%d",&t); 19 while(t--){ 20 int n;scanf("%d",&n); 21 for(int i=0;i<2;++i)for(int j=0;j<2;++j)has[i][j].clear(); 22 for(int i=0;i<n;++i){ 23 scanf("%d%d",&x[i],&y[i]); 24 has[abs(x[i])%2][abs(y[i])%2].push_back(i); 25 } 26 int a=0,b=0; 27 for(int i=0;i<2;++i)for(int j=0;j<2;++j) 28 if(has[i][j].size()>has[a][b].size())a=i,b=j; 29 int ne=n/10; ans.clear(); uu.clear(); 30 for(int i=0;i<has[a][b].size();++i){ 31 for(int j=i+1;j<has[a][b].size();++j){ 32 int l=has[a][b][i],r=has[a][b][j]; 33 if(l+1==r||l==0&&r==n-1)continue; 34 int mx=(x[l]+x[r])>>1,my=(y[l]+y[r])>>1; 35 if(!uu.count(make_pair(mx,my))){ 36 ans.push_back(make_pair(mx,my)); 37 uu.insert(make_pair(mx,my)); 38 --ne; 39 } 40 if(!ne)break; 41 } 42 if(!ne)break; 43 } 44 for(int i=0;i<ans.size();++i)printf("%d %d\n",ans[i].first,ans[i].second); 45 } 46 return 0; 47 }
安慰一下自己emm我的代码比zk的多适用与正方性那种样例(非凸包)orz