题目链接:http://poj.org/problem?id=1228
题目大意:给定n个二维坐标系中的点,这些点是一个凸多边形顶点的子集。判断这些点是否能唯一确定原来的凸多边形。
解题思路:
考虑多边形的一条边。对于给定两个端点,连起来变成一条边。原来的多边形中可能在这条边之外还有一个端点p',那么这条边在原多边形中可能就不存在。 那么怎么能确定一条边一定在原多边形中存在呢?
另外这道题也有需要特判的情况,以上的分析都基于能求出一个多边形凸包,实际上这样的凸包可能并不存在。我使用的是xy坐标排序的graham-scan算法,如果所有的点都共线的话,算法依然会求出一个点集(包括了输入的所有的点),并且可能判断出点集中每条的线段上都有第三个顶点,然后输出YES。实际上很显然,直线外可能存在其他的顶点把这条线“撑开”,答案应该是NO。所以必须特判所有顶点共线的情况。按照惯例,考虑n的极限情况,n=1时,只有一个顶点。这时候判断共线的函数会把这种情况判断成共线,所以n=1时也没有问题。
至于复杂度: 求凸包O(nlogn),判断唯一性时枚举每条边和每个点,O(nh),其中h是凸包中点的数目。所以总复杂度在O(nlogn)和O(nh)之间。
/*
* 2014.11.8
* Problem: 1228
* Memory: 204K
* Time: 16MS
*/
#include "stdio.h"
#include "math.h"
#define EPS 1e-8
#define MAXN 2007
struct Point {
double x, y;
}p[MAXN], c[MAXN];
int n, top;
void sort(int l, int r) {
int i=l, j=r;
double x = p[(l+r)>>1].x, y = p[(l+r)>>1].y;
do {
while (p[i].x<x || (p[i].x==x && p[i].y<y)) i++;
while (p[j].x>x || (p[j].x==x && p[j].y>y)) j--;
if (i<=j) {
Point t = p[i]; p[i] = p[j]; p[j] = t;
i++; j--;
}
} while (i<=j);
if (i<r) sort(i, r);
if (l<j) sort(l, j);
}
double max(double a, double b) {return a>b?a:b;}
double min(double a, double b) {return a>b?b:a;}
// 叉积
double cross(Point a, Point b, Point c) {
double x1 = b.x-a.x, y1 = b.y-a.y;
double x2 = c.x-a.x, y2 = c.y-a.y;
return x1*y2-x2*y1;
}
// 判断是否左转, 包括共线情况
bool turnLeft(Point a, Point b, Point c) {
return cross(a, b, c)>=-EPS;
}
// 求凸包
void convexHull() {
sort(1, n);
top = 1; c[top] = p[1];
for (int i=2;i<=n;i++) {
while (top>=2 && turnLeft(c[top-1], c[top], p[i])) top--;
c[++top] = p[i];
}
int pn = top;
for (int i=n-1;i>=1;i--) {
while (top>=pn+1 && turnLeft(c[top-1], c[top], p[i])) top--;
c[++top] = p[i];
}
top --;
}
// 判断点是否在线段上,不包括端点
bool onSegment(Point a, Point b, Point c) {
if (fabs(cross(a, b, c))<EPS &&
min(a.x,b.x)<=c.x && c.x<=max(a.x,b.x) &&
min(a.y,b.y)<=c.y && c.y<=max(a.y,b.y) &&
!((c.x==a.x&&c.y==a.y)||(c.x==b.x&&c.y==b.y)))
return true;
return false;
}
// 验证凸包中的每一条边是否确定
bool verify() {
c[top+1] = c[1];
for (int i=1;i<=top;i++) {
bool on = false;
for (int j=1;j<=n;j++)
if (onSegment(c[i], c[i+1], p[j])) {
on = true;
break;
}
if (!on) return false;
}
return true;
}
// 判断所有点是否共线
bool collinear() {
p[n+1] = p[1];
p[n+2] = p[2];
for (int i=1;i<=n;i++)
if (fabs(cross(p[i],p[i+1],p[i+2]))>EPS) return false;
return true;
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
scanf("%d", &n);
for (int i=1;i<=n;i++) scanf("%lf %lf", &p[i].x, &p[i].y);
// 判断顶点是否共线,如果共线直接输出NO
if (collinear()) {
printf("NO\n");
continue;
}
// 求凸包, 凸包上的点保存在 c 数组中
convexHull();
// 判断凸包上的点是否能唯一确定一个凸多边形
// 即判断凸包每条线段上都有除了端点外的点
bool only = verify();
if (only) {
printf("YES\n");
} else {
printf("NO\n");
}
}
return 0;
}