链接:https://ac.nowcoder.com/acm/contest/883/H
来源:牛客网
题目描述
There are always some problems that seem simple but is difficult to solve.
ZYB got N distinct points on a two-dimensional plane. He wants to draw a magic line so that the points will be divided into two parts, and the number of points in each part is the same. There is also a restriction: this line can not pass through any of the points.
Help him draw this magic line.输入描述:
There are multiple cases. The first line of the input contains a single integer T (1≤T≤10000), indicating the number of cases.
For each case, the first line of the input contains a single even integer N (2≤N≤1000), the number of points. The following $N$ lines each contains two integers xi,yi (∣xi,yi∣≤1000)
It is guaranteed that the sum of N over all cases does not exceed 2×105.输出描述:
For each case, print four integers x1,y1,x2,y2 in a line, representing a line passing through (x1,y1) and (x2,y2). Obviously the output must satisfy (x1,y1)≠(x2,y2).
The absolute value of each coordinate must not exceed 109. It is guaranteed that at least one solution exists. If there are multiple solutions, print any of them.
示例1
输入
1 4 0 1 -1 0 1 0 0 -1
输出
-1 999000000 1 -999000001
- 题意
给定平面上一系列的点,求一条以(x1,y1),(x2,y2)两点表示的直线将平面分为包含点数量相等的两部分,其中直线不能穿过任何一点。
- 解题思路
计算几何问题。
示例中的坐标不是随便得到的,将x、y中其中一方拉长到在109以内尽可能大的数,能让直线尽可能水平或者竖直,由于坐标都为整数,这样取值可以使得直线不穿过任何一点,为方便我们取尽可能水平,即x绝对值尽可能大。应该先以y升序,x降序将这些点排序,然后取中位数的两点,若这两点不在同一水平线上,则将这两点的x值扩大;若这两点在同一水平线上,则先将这两点的y坐标一上一下错开,再扩大x的值。
- 错误代码
最开始考虑完整穿过其他点的所有情况,将直线直接取在了中位点上,又为了防止穿过点而将所取点向上下左右错开,直到所取两点不在给出点集合中。但这种情况仅仅是保证了我所取到的点不在点集中,并不能保证他们所连直线是否穿过别的点。
错误代码如下:
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 struct p 5 { 6 ll x,y; 7 }ps[1002]; 8 bool operator <(struct p a,struct p b) 9 { 10 if (a.x!=b.x) return a.x<b.x; 11 else return a.y<b.y; 12 } 13 bool operator ==(struct p a,struct p b) 14 { 15 return a.x==b.x&&a.y==b.y; 16 } 17 bool operator !=(struct p a,struct p b) 18 { 19 return a.x!=b.x||a.y!=b.y; 20 } 21 set <struct p> points; 22 bool cmpx(struct p a,struct p b) 23 { 24 return a.x<b.x; 25 } 26 bool cmpy(struct p a,struct p b) 27 { 28 return a.y<b.y; 29 } 30 int main() 31 { 32 ll t,n; 33 scanf("%lld",&t); 34 while(t--){ 35 ll x1,y1,x2,y2; 36 scanf("%lld",&n); 37 for (ll i=0;i<n;i++){ 38 scanf("%lld %lld",&ps[i].x,&ps[i].y); 39 points.insert(ps[i]); 40 } 41 sort(ps,ps+n,cmpx); 42 x1=ps[n/2-1].x; 43 x2=ps[n/2].x; 44 sort(ps,ps+n,cmpy); 45 y1=ps[n/2-1].y; 46 y2=ps[n/2].y; 47 struct p p1,p2; 48 p1.x=x1;p1.y=y2; 49 p2.x=x2;p2.y=y2; 50 if (p1==p2){ 51 (p1.x)--; 52 (p1.y)--; 53 (p2.x)++; 54 (p2.y)++; 55 } 56 while(points.find(p1)!=points.end()||points.find(p2)!=points.end()){ 57 (p1.x)--; 58 (p2.x)++; 59 } 60 printf("%lld %lld %lld %lld\n",p1.x,p1.y,p2.x,p2.y); 61 } 62 return 0; 63 }
- AC代码
在发现题目的真正做法之后还在另一个地方wa了好多遍,这道题对点的排序也有讲究。先前我们的排序方法是x升序,y升序,但拉长x值的时候是将较大x值向上拉长,较小x值向下拉长。这样选取点的话会造成选取的部分并没有被分开。
即如果我们选择将较大x值向上拉长,较小x值向下拉长的话,这条直线应该是向右上方倾斜的,将平面分成的是左上和右下的两部分,选取点时也自然应该从下到上,从右到左。
AC代码如下:
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 struct p 5 { 6 ll x,y; 7 }ps[1002]; 8 bool cmpx(struct p a,struct p b) 9 { 10 return a.x<b.x; 11 } 12 bool cmpy(struct p a,struct p b) 13 { 14 if(a.y!=b.y) return a.y<b.y; 15 else return a.x>b.x; 16 } 17 int main() 18 { 19 ll t,n; 20 scanf("%lld",&t); 21 while(t--){ 22 ll x1,y1,x2,y2; 23 scanf("%lld",&n); 24 for (ll i=0;i<n;i++){ 25 scanf("%lld %lld",&ps[i].x,&ps[i].y); 26 } 27 sort(ps,ps+n,cmpy); 28 x1=(ps[n/2-1].x); 29 y1=ps[n/2-1].y; 30 x2=(ps[n/2].x); 31 y2=ps[n/2].y; 32 x1-=999000000; 33 x2+=999000000; 34 if (y1==y2) { 35 y1-=1; 36 y2+=1; 37 } 38 printf("%lld %lld %lld %lld\n",x1,y1,x2,y2); 39 } 40 return 0; 41 }