题目概述
按顺时针序给定一个简单N边形上所有顶点的坐标x,y,问其是否有核
时限
2000ms/6000ms
输入
第一行正整数times,其后有times组数据,每组数据第一个正整数N,其后N对数整数x,y,每对数描述一个顶点坐标
限制
3<=N<=100
输出
每行一个字符串,若存在核,输出YES,否则NO
样例输入
2
4 0 0 0 1 1 1 1 0
8 0 0 0 2 1 2 1 1 2 1 2 2 3 2 3 0
样例输出
YES
NO
讨论
计算几何,半平面交,算是第一次尝试,说说半平面交的基本思路(其实网上的资料哪个写的都比额好),将多边形上所有点按顺时针或逆时针排序(本题为顺时针),初始化一个点集的点为原来多边形上所有点,然后过多边形相邻两个点做直线,用于判定,比较点集中的点是否在直线右侧或直线上,在则加入另一个点集,不在则考察点集中这个点前后各一个点与这个点所成的直线是否与判定直线有交点,把交点加入新的点集,将点集中所有点都处理过一次后,将新点集的点赋给点集,然后从多边形上取下一条判定线,继续操作,最后,如果点集都空了,那就是没有核,否则就是有
这个似乎是平方级的方法,作为第一次尝试,暂且先如此
需要说明一点,从一般情况而言,由于半平面交需要求新的交点,而使得点坐标不一定为整数,因而点的结构采用两个整数实际上是不够严谨的行为,应当采用double,但由于是求存在性,不会过于纠结小数部分(否则可能会由于浮点误差而变成坑人数据),因而采用整型也能蒙混过关
题解状态
148K,0MS,C++,2261B
题解代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define INF 0x3f3f3f3f
#define MAXN 103
#define memset0(a) memset(a,0,sizeof(a))
#define EPS 1e-6
struct Pt//point 点的结构
{
int x, y;//注意这里不应用整型
}pts[MAXN], pt1[MAXN], pt2[MAXN];//分别记录原始数据 以及求半平面交的两个辅助数组
int hpt1, hpt2;//half_plane_point 配合pt1和pt2数组 点集中剩余点数
int N;//多边形边数
int xp(int x1, int y1, int x2, int y2, int x3, int y3)
{
return (x1 - x2)*(y3 - y2) - (y1 - y2)*(x3 - x2);
}
Pt point_of_intersection(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4)//求交点 基本思想和以前一样 不过改用返回值返回一个点了
{
Pt poi;
if (abs(x1 - x2) < EPS) {
double k3 = (y4 - y3) / (x4 - x3);
poi.x = x1, poi.y = y3 - k3*x3 + k3*x1;
}
else if (abs(x3 - x4) < EPS) {
double k1 = (y2 - y1) / (x2 - x1);
poi.x = x3, poi.y = y1 - k1*x1 + k1*x3;
}
else {
double k1 = (y2 - y1) / (x2 - x1);
double k3 = (y4 - y3) / (x4 - x3);
poi.x = (y1 - y3 - k1*x1 + k3*x3) / (k3 - k1);
poi.y = (k1*k3*(x1 - x3) - k3*y1 + k1*y3) / (k1 - k3);
}
return poi;
}
void fun()
{
for (int p = 0; p < N; p++) {
scanf("%d%d", &pts[p].x, &pts[p].y);//input
pt1[p] = pts[p];//顺便初始化点集
}
hpt1 = N;//一开始点集1中有N个点
for (int p = 0; p < N; p++) {//选择多边形上相邻的两个点做判定直线
hpt2 = 0;//点集2初始为0个点
for (int i = 0; i < hpt1; i++) {//对于点集1中的点
if (xp(pt1[i].x, pt1[i].y, pts[p].x, pts[p].y, pts[(p + 1) % N].x, pts[(p + 1) % N].y) >= 0)//在判定线右侧的
pt2[hpt2++] = pt1[i];//加入点集2
else {//否则
if (xp(pt1[(i - 1 + hpt1) % hpt1].x, pt1[(i - 1 + hpt1) % hpt1].y, pts[p].x, pts[p].y, pts[(p + 1) % N].x, pts[(p + 1) % N].y)>0) {//如果点集1中前一个点在判定线右侧
pt2[hpt2++] = point_of_intersection(pts[p].x, pts[p].y, pts[(p + 1) % N].x, pts[(p + 1) % N].y, pt1[i].x, pt1[i].y, pt1[(i - 1 + hpt1) % hpt1].x, pt1[(i - 1 + hpt1) % hpt1].y);//将前一个点和这个点所成直线与判定线的交点加入点集2
}
if (xp(pt1[(i + 1) % hpt1].x, pt1[(i + 1) % hpt1].y, pts[p].x, pts[p].y, pts[(p + 1) % N].x, pts[(p + 1) % N].y)>0) {//对点集1中下一个点同样处理
pt2[hpt2++] = point_of_intersection(pts[p].x, pts[p].y, pts[(p + 1) % N].x, pts[(p + 1) % N].y, pt1[i].x, pt1[i].y, pt1[(i + 1) % hpt1].x, pt1[(i + 1) % hpt1].y);
}
}
}
for (int p = 0; p < hpt2; p++)
pt1[p] = pt2[p];
hpt1 = hpt2;//更新点集1的数据
}
if (!hpt1)//点集1完全空了
printf("NO\n");//output
else
printf("YES\n");//output
}
int main(void)
{
//freopen("vs_cin.txt", "r", stdin);
//freopen("vs_cout.txt", "w", stdout);
int times;
scanf("%d", ×);//input
while (times--) {
scanf("%d", &N);//input
fun();
}
}
EOF