题目链接:点击这里
题意:一个多边形硬币和多边形小孔,能不能让硬币以某种姿势落下通过小孔。
显然硬币是竖直落下最优,所以需要找到硬币投影线段的最短长度,如果数据比较大就要使用旋转卡壳,因为数据比较小所以直接求个凸包暴力扫描。然后需要找到小孔的最长的一条线段,这个线段肯定通过小孔的两个端点,所以直接暴力枚举小孔的两个端点,构成一条直线,求出这个直线和小孔所有的交点,然后给交点排个序,相邻交点间的线段判断一下是在小孔内还是小孔外,可以取线段的中点判断是不是在多边形内部做到。然后取小孔内最长的连续一段。最后只需要小孔内最长的线段比硬币的最短投影长就合法。
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
#define maxn 1005
const double eps = 1e-8;
const double INF = 1e20;
const double pi = acos (-1.0);
int dcmp (double x) {
if (fabs (x) < eps) return 0;
return (x < 0 ? -1 : 1);
}
inline double sqr (double x) {return x*x;}
//*************点
struct Point {
double x, y;
Point (double _x = 0, double _y = 0):x(_x), y(_y) {}
void input () {scanf ("%lf%lf", &x, &y);}
void output () {printf ("%.2f %.2f\n", x, y);}
bool operator == (const Point &b) const {
return (dcmp (x-b.x) == 0 && dcmp (y-b.y) == 0);
}
bool operator < (const Point &b) const {
return (dcmp (x-b.x) == 0 ? dcmp (y-b.y) < 0 : x < b.x);
}
Point operator + (const Point &b) const {
return Point (x+b.x, y+b.y);
}
Point operator - (const Point &b) const {
return Point (x-b.x, y-b.y);
}
Point operator * (double a) {
return Point (x*a, y*a);
}
Point operator / (double a) {
return Point (x/a, y/a);
}
double len2 () {//返回长度的平方
return sqr (x) + sqr (y);
}
double len () {//返回长度
return sqrt (len2 ());
}
Point change_len (double r) {//转化为长度为r的向量
double l = len ();
if (dcmp (l) == 0) return *this;//零向量返回自身
r /= l;
return Point (x*r, y*r);
}
Point rotate_left () {//顺时针旋转90度
return Point (-y, x);
}
Point rotate_right () {//逆时针旋转90度
return Point (y, -x);
}
Point rotate (Point p, double ang) {//绕点p逆时针旋转ang
Point v = (*this)-p;
double c = cos (ang), s = sin (ang);
return Point (p.x + v.x*c - v.y*s, p.y + v.x*s + v.y*c);
}
Point normal () {//单位法向量
double l = len ();
return Point (-y/l, x/l);
}
};
double cross (Point a, Point b) {//叉积
return a.x*b.y-a.y*b.x;
}
double dot (Point a, Point b) {//点积
return a.x*b.x + a.y*b.y;
}
double dis (Point a, Point b) {//两个点的距离
Point p = b-a; return p.len ();
}
double rad (Point a, Point b) {//两个向量的夹角
return fabs (atan2 (fabs (cross (a, b)), dot (a, b)) );
}
bool parallel (Point a, Point b) {//向量平行
double p = rad (a, b);
return dcmp (p) == 0 || dcmp (p-pi) == 0;
}
//************直线 线段
struct Line {
Point s, e;//直线的两个点
Line () {}
Line (Point _s, Point _e) {
s = _s, e = _e;
}
void input () {
s.input ();
e.input ();
}
void output () {
printf ("%.2f,%.2f %.2f,%.2f\n", s.x, s.y, e.x, e.y);
}
void adjust () {
if (e < s) swap (e, s);
}
double length () {//求线段长度
return dis (s, e);
}
};
int relation (Point p, Line l) {//点和直线的关系
//1:在左侧 2:在右侧 3:在直线上
int c = dcmp (cross (p-l.s, l.e-l.s));
if (c < 0) return 1;
else if (c > 0) return 2;
else return 3;
}
bool point_on_seg (Point p, Line l) {//判断点在线段上
return dcmp (cross (p-l.s, l.e-l.s)) == 0 &&
dcmp (dot (p-l.s, p-l.e) <= 0);
//如果忽略端点交点改成小于号就好了
}
bool parallel (Line a, Line b) {//直线平行
return parallel (a.e-a.s, b.e-b.s);
}
int seg_cross_seg (Line a, Line v) {//线段相交判断
//2:规范相交 1:不规范相交 0:不相交
int d1 = dcmp (cross (a.e-a.s, v.s-a.s));
int d2 = dcmp (cross (a.e-a.s, v.e-a.s));
int d3 = dcmp (cross (v.e-v.s, a.s-v.s));
int d4 = dcmp (cross (v.e-v.s, a.e-v.s));
if ((d1^d2) == -2 && (d3^d4) == -2) return 2;
return (d1 == 0 && dcmp (dot (v.s-a.s, v.s-a.e)) <= 0) ||
(d2 == 0 && dcmp (dot (v.e-a.s, v.e-a.e)) <= 0) ||
(d3 == 0 && dcmp (dot (a.s-v.s, a.s-v.e)) <= 0) ||
(d4 == 0 && dcmp (dot (a.e-v.s, a.e-v.e)) <= 0);
}
int line_cross_seg (Line a, Line v) {//直线和线段相交判断 a直线v线段
//2:规范相交 1:非规范相交 0:不相交
int d1 = dcmp (cross (a.e-a.s, v.s-a.s));
int d2 = dcmp (cross (a.e-a.s, v.e-a.s));
if ((d1^d2) == -2) return 2;
return (d1 == 0 || d2 == 0);
}
int line_cross_line (Line a, Line v) {//直线相交判断
//0:平行 1:重合 2:相交
if (parallel (a, v)) return relation (a.e, v) == 3;
return 2;
}
Point line_intersection (Line a, Line v) {//直线交点
//调用前确保有交点
double a1 = cross (v.e-v.s, a.s-v.s);
double a2 = cross (v.e-v.s, a.e-v.s);
return Point ((a.s.x*a2-a.e.x*a1)/(a2-a1), (a.s.y*a2-a.e.y*a1)/(a2-a1));
}
double point_to_line (Point p, Line a) {//点到直线的距离
return fabs (cross (p-a.s, a.e-a.s) / a.length ());
}
double point_to_seg (Point p, Line a) {//点到线段的距离
if (dcmp (dot (p-a.s, a.e-a.s)) < 0 || dcmp (dot (p-a.e, a.s-a.e)) < 0)
return min (dis (p, a.e), dis (p, a.s));
return point_to_line (p, a);
}
//*************多边形
bool relation (Point q, Point *p, int n) {//点和多边形的关系(凸凹都可以)
//0:外部 1:内部 2:边上 3:顶点
for (int i = 0; i < n; i++) {
if (p[i] == q)
return 3;
}
for (int i = 0; i < n; i++) {
if (point_on_seg (q, Line (p[i], p[(i+1)%n])))
return 2;
}
int cnt = 0;
for (int i = 0; i < n; i++) {
int j = (i+1)%n;
int k = dcmp (cross (q-p[j], p[i]-p[j]));
int u = dcmp (p[i].y-q.y);
int v = dcmp (p[j].y-q.y);
if (k > 0 && u < 0 && v >= 0) cnt++;
if (k < 0 && v < 0 && u >= 0) cnt--;
}
return cnt != 0;
}
int convex_hull (Point *p, Point *ch, int n) {//求凸包
//所有的点集 凸包点集 点集的点数
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-1]) <= 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-1]) <= 0)
m--;
ch[m++] = p[i];
}
if (n > 1)
m--;
return m;
}
Point coin[maxn], hole[maxn], ch[maxn];
int n, m;
double poly_min_seg (Point *coin, int m) {
//多边形的最短长度 可以是凹 复杂度O(n^2)
//相当于硬币投影的最短长度
double ans = INF;
int cnt = convex_hull (coin, ch, m);
for (int i = 0; i < cnt; i++) {
int j = (i+1)%cnt;
double tmp = 0;
for (int k = 0; k < cnt; k++) if (k != i && k != j) {
tmp = max (tmp, point_to_line (ch[k], Line (ch[i], ch[j])));
}
ans = min (ans, tmp);
}
return ans;
}
double poly_max_seg (Point *hole, int n) {
//多边形的最长长度 可以是凹 复杂度O(n^3)
//相当于孔洞提供的最长内部线段
double ans = 0;
vector <Point> gg;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) if (i != j) {
Line cur (hole[i], hole[j]);
gg.clear ();
for (int x = 0; x < n; x++) {
int y = (x+1)%n;
int in1, in2;
in1 = relation (hole[x], cur);
in2 = relation (hole[y], cur);
if (in1 == 3) gg.push_back (hole[x]);
if (in2 == 3) gg.push_back (hole[y]);
if (in1 == 3 || in2 == 3) {
continue;
}
int id = line_cross_seg (cur, Line (hole[x], hole[y]));
if (id == 2) {
Point p = line_intersection (cur, Line (hole[x], hole[y]));
}
}
sort (gg.begin (), gg.end ());
int Max = gg.size ();
double tmp = 0;
for (int i = 0; i < Max-1; i++) {
Point p = Point (gg[i].x+gg[i+1].x, gg[i].y+gg[i+1].y)/2;
int id = relation (p, hole, n);
if (id == 0) {
ans = max (ans, tmp);
tmp = 0;
}
else {
tmp += dis (gg[i], gg[i+1]);
}
}
ans = max (ans, tmp);
}
}
return ans;
}
int main () {
int t;
scanf ("%d", &t);
while (t--) {
scanf ("%d", &n);
for (int i = 0; i < n; i++) hole[i].input ();
scanf ("%d", &m);
for (int i = 0; i < m; i++) coin[i].input ();
double ans1 = poly_max_seg (hole, n);//孔能提供的最大直径
double ans2 = poly_min_seg (coin, m);//硬币能提供的最小直径
printf ("%s\n", ans2 <= ans1 ? "legal" : "illegal");
}
return 0;
}