[codevs 1302] 小矮人(2002年CEOI中欧信息学奥赛)
描述
矮人们平时有走亲访友的习惯。一天,矮人国要修一条高速公路,矮人们希望他们走亲访友的时候,能够不必穿越高速公路,这样会更安全一些。现在有M个高速公路的修建方案,请你判断这M条高速功能是否能满足矮人们的期望。也就是说给出平面上的N个点(矮人们的住所位置),对于M条直线(高速公路),依次判断这N个点是否在每条直线的同一侧。是输出GOOD,不是输出BAD。
后来从刘汝佳的书里发现这个算法叫旋转卡壳,专门求对踵点的。
题解
首先可以想出一个凸包模型来,因为求出所有点的凸包后可以判断直线如果穿过凸包,就一定不满足题意。
问题是怎么判断直线是否穿过凸包呢?如果直线穿过凸包,就一定在凸包两个最远点的中间,最远点可以通过下面的方法找:首先预处理记录下凸包每个边的斜率,再算出直线的斜率,将斜率从小到大排列,假设直线斜率是正的,二分查找第一个大于直线斜率的边,它的起点一定是一个最远点,可以画图验证一下,因为他后面的边斜率比它大,也就相当于走的离凸包中心越来越近。然后把斜率取相反数,再找第一个斜率大于它的边,起点是另一个最远点。
在实现时直接用atan2()函数计算角度不用算斜率。
代码:
总时间耗费: 808ms
总内存耗费: 4 MB
#include<cstdio>
#include<cmath>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn = 100000 + 10;
const double PI = acos(double(-1));
int n;
struct Point {
double x, y;
Point(double x=0, double y=0):x(x),y(y) {}
}p[maxn], ch[maxn];
typedef Point Vector;
Vector operator + (Vector A, Vector B) { return Vector(A.x+B.x, A.y+B.y); }
Vector operator - (Vector A, Vector B) { return Vector(A.x-B.x, A.y-B.y); }
Vector operator * (Vector A, double p) { return Vector(A.x*p, A.y*p); }
Vector operator / (Vector A, double p) { return Vector(A.x/p, A.y/p); }
bool operator < (const Vector& a, const Vector& b) {
return a.x < b.x || (a.x == b.x && a.y < b.y);
}
const double eps = 1e-10;
int dcmp(double x) {
if(fabs(x) < eps) return 0; else return x < 0 ? -1 : 1;
}
bool dcmp2(const double& a, const double& b) {
if(dcmp(b-a) == 1) return 1;
return 0;
}
bool operator == (const Vector& a, const Vector& b) {
return dcmp(a.x-b.x) == 0 && dcmp(a.y-b.y) == 0;
}
double angle(Vector v) { double ret = atan2(v.y, v.x); return ret < -PI/2 ? ret+2*PI : ret; }
double Cross(Vector A, Vector B) { return A.x*B.y - A.y*B.x; }
int ConvexHull() {
sort(p, p+n);
int m = 0;
for(int i = 0; i < n; i++) {
while(m > 1 && Cross(ch[m-1]-ch[m-2], p[i]-ch[m-2]) <= 0) m--;
ch[m++] = p[i];
}
int k = m;
for(int i = n-2; i >= 0; i--) {
while(m > k && Cross(ch[m-1]-ch[m-2], p[i]-ch[m-2]) <= 0) m--;
ch[m++] = p[i];
}
if(n > 1) m--;
return m;
}
double ang[maxn];
int main() {
scanf("%d", &n);
for(int i = 0; i < n; i++)
scanf("%lf%lf", &p[i].x, &p[i].y);
int c = ConvexHull();
for(int i = 0; i < c; i++) ang[i] = angle(ch[i+1]-ch[i]);
Point a, b;
while(scanf("%lf%lf%lf%lf", &a.x, &a.y, &b.x, &b.y) == 4) {
if(n <= 1) printf("GOOD\n");
else {
Point u = ch[upper_bound(ang, ang+c, angle(b-a), dcmp2)-ang];
Point v = ch[upper_bound(ang, ang+c, angle(a-b), dcmp2)-ang];
if(dcmp(Cross(b-a, u-a)*Cross(b-a, v-a)) < eps) printf("BAD\n");
else printf("GOOD\n");
}
}
return 0;
}