【CCPC】2022威海站 C. Grass | 计算几何

184 阅读2分钟

本文已参与「新人创作礼」活动, 一起开启掘金创作之路。

【CCPC】2022威海站 C. Grass | 计算几何

题目链接

Problem - C - Codeforces

题目

image.png

题目大意

给出二维平面内的 nn 个点,对 1in1\le i \le nPi=(xi,yi)P_i=(x_i,y_i),求 5 个不同的点 Pa,Pb,Pc,Pd,PeP_a,P_b,P_c,P_d,P_e 满足 PaP_a 分别与其他四个点的连线仅存在 PaP_a 一个交点。

思路

显然除非 5 点共线,否则这 5 个点一定可以选出一个作为 PaP_a 且满足题意。

则我们先选中输入的前 4 个点,枚举选择其他 n4n-4 个点作为第 5 个点的情况。如果不管选择哪一个点都 5 点共线,则说明所有给定的点都在一条直线上,不管怎样选择都不合题意。否则我们找到了一组合法的点。

我们可以直接枚举选中的 5 个点谁作为 PaP_a,顺便就能剔除 5 点共线的情况:求出 PaP_a 指向其他四个点的向量,枚举四个向量中的任意两个,判断它们是否共线且同向。因为只有 5 个点,所以虽然判断方法很暴力也不会超时。

怎样判断两个向量 (x1,y1)(x_1,y_1)(x2,y2)(x_2,y_2) 共线且同向呢?
首先,x1x2=y1y2\frac{x_1}{x_2}=\frac{y_1}{y_2} 说明两个向量共线,为避免除零,我们可以将其转化为 x1×y2=x2×y1x_1\times y_2=x_2\times y_1。如果两个向量共线,则如果他们的横纵坐标符号均具有相同的符号则同向。

找到了一组合法的点按题意输出并停止寻找,最终都没有找到合法的点集输出无解。

代码

#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;
}