本文已参与「新人创作礼」活动, 一起开启掘金创作之路。
【CCPC】2022威海站 C. Grass | 计算几何
题目链接
题目
题目大意
给出二维平面内的 个点,对 记 ,求 5 个不同的点 满足 分别与其他四个点的连线仅存在 一个交点。
思路
显然除非 5 点共线,否则这 5 个点一定可以选出一个作为 且满足题意。
则我们先选中输入的前 4 个点,枚举选择其他 个点作为第 5 个点的情况。如果不管选择哪一个点都 5 点共线,则说明所有给定的点都在一条直线上,不管怎样选择都不合题意。否则我们找到了一组合法的点。
我们可以直接枚举选中的 5 个点谁作为 ,顺便就能剔除 5 点共线的情况:求出 指向其他四个点的向量,枚举四个向量中的任意两个,判断它们是否共线且同向。因为只有 5 个点,所以虽然判断方法很暴力也不会超时。
怎样判断两个向量 和 共线且同向呢?
首先, 说明两个向量共线,为避免除零,我们可以将其转化为 。如果两个向量共线,则如果他们的横纵坐标符号均具有相同的符号则同向。
找到了一组合法的点按题意输出并停止寻找,最终都没有找到合法的点集输出无解。
代码
#include <iostream>
#include <algorithm>
#include <math.h>
#include <stdio.h>
#include <map>
#include <vector>
#include <queue>
using namespace std;
using LL=long long;
const int N=500001;
struct point{
LL x,y;
point operator - (point a)
{
return {x-a.x,y-a.y};
}
bool operator ^ (point a)
{
if (x*a.y!=y*a.x) return 0;
if (x*a.x<0||y*a.y<0) return 0;
return 1;
}
}p[N],c[6],vec[5];
int n,m;
int check()
{
for (int i=1;i<=5;++i)
{
int tot=0;
for (int j=1;j<=5;++j)
if (i!=j) vec[++tot]=c[i]-c[j];
int flag=1;
for (int j=1;j<=4&&flag;++j)
for (int k=1;k<=4&&flag;++k)
if (j!=k&&(vec[j]^vec[k])) flag=0;
if (!flag) continue;
printf("YES\n");
printf("%lld %lld\n",c[i].x,c[i].y);
for (int j=1;j<=5;++j)
if (i!=j) printf("%lld %lld\n",c[j].x,c[j].y);
return 1;
}
return 0;
}
LL solve()
{
scanf("%d",&n);
for (int i=1;i<=n;++i) scanf("%lld%lld",&p[i].x,&p[i].y);
if (n<5) return printf("NO\n"),0;
for (int i=1;i<=4;++i) c[i]=p[i];
for (int i=5;i<=n;++i)
{
c[5]=p[i];
if (check()) return 0;
}
printf("NO\n");
return 0;
}
int main()
{
int T=1;
scanf("%d",&T);
while (T--) solve();
return 0;
}