题目概述
给定N条线段端点坐标,问是否存在一直线,使所有线段在该直线上的投影有公共点
时限
1000ms/3000ms
输入
第一行正整数times,其后times组数据,每组数据第一行正整数N,其后N行,每行4个浮点数,每2个表示1个端点坐标
限制
1<=N<=100;浮点相对误差限EPS=1e-8
输出
每行一个字符串,若存在,为Yes!,否则为No!
样例输入
3
2
1.0 2.0 3.0 4.0
4.0 5.0 6.0 7.0
3
0.0 0.0 0.0 1.0
0.0 1.0 0.0 2.0
1.0 1.0 2.0 1.0
3
0.0 0.0 0.0 1.0
0.0 2.0 0.0 3.0
1.0 1.0 2.0 1.0
样例输出
Yes!
Yes!
No!
讨论
计算几何,题目直接做很麻烦,需要转化,首先要明确两点,其一,若存在题目要求的直线,则必然存在一条与之垂直的直线和所有线段相交,其二,若存在一条与所有直线相交的直线,则必然可通过旋转平移使其经过至少2个线段的端点(可能是同一条线段的),那么反过来利用这两条结论,只要枚举所有不重合的线段端点,构造直线,如果和所有线段相交,那就是存在了
今天刚开始接手计算几何,对一些细节不是很熟练,algorithm中包含math.h,但math.h中的abs是针对整数的,而cmath重载之后也可应用于浮点数,由于编译器太人性化,没有报错,足足贡献3个WA才发现这事
题解状态
180K,16MS,C++,1229B
题解代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>//重载的abs在这里
using namespace std;
#define INF 0x3f3f3f3f
#define MAXN 103
#define memset0(a) memset(a,0,sizeof(a))
#define EPS 1e-8
double x[MAXN * 2], y[MAXN * 2];//分别存放横纵坐标 一条线段2个点 所以需要双倍空间
inline double xp(double x1, double y1, double x2, double y2, double x3, double y3)//cross_product 向量积 以点2为公共起点
{
return (x1 - x2)*(y3 - y2) - (y1 - y2)*(x3 - x2);
}
inline bool intersect(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4)//相交 由于是直线和线段关系 因而只要线段端点在直线两侧或有一个在直线上就算相交 默认前两点为直线的
{
return xp(x3, y3, x1, y1, x2, y2)*xp(x4, y4, x1, y1, x2, y2) <= EPS;
}
void fun(int N)
{
for (int p = 0; p < N; p++)
scanf("%lf%lf%lf%lf", &x[p], &y[p], &x[N + p], &y[N + p]);//input
for (int p = 0; p < 2 * N; p++)
for (int i = p + 1; i < 2 * N; i++) {//枚举所有端点 找出2个做直线
if (abs(x[p] - x[i]) < EPS&&abs(y[p] - y[i]) < EPS)
continue;//重合的点就算了
bool no = 0;//是否存在直线与线段不相交的情况 初值不存在
for (int u = 0; u < N; u++) {
if (!intersect(x[p], y[p], x[i], y[i], x[u], y[u], x[u + N], y[u + N])) {
no = 1;
break;//有不相交的就要中断 继续枚举端点
}
}
if (!no) {//全部相交 存在
printf("Yes!\n");//output
return;
}
}
printf("No!\n");//output//都有不相交的情况 不存在
}
int main(void)
{
//freopen("vs_cin.txt", "r", stdin);
//freopen("vs_cout.txt", "w", stdout);
int times;
scanf("%d", ×);//input
while (times--) {
int N;
scanf("%d", &N);//input
fun(N);
}
}
EOF