题目概述
平面坐标系内有若干个图形,可能的种类为正方形(square),矩形(rectangle),线段(line),三角形(triangle),简单多边形(polygon,其所有边不互相交,且所有边长为正),给出正方形一条对角线的两个端点,长方形三个顶点(必可以第二个点为顶点构成直角),线段两个端点,三角形三个顶点,简单多边形所有顶点(按顺时针或逆时针),且给出每个图形的名称(一大写英文字母),求每个图形与其他哪些图形相交
封闭图形的内部不属于该图形,即完全包含不属于相交
时限
2000ms/6000ms
输入
每组数据含若干行,每行第一个大写字母,为该图形名称,其后一个字符串,描述该图形类别(可能的类别已在上面给出),除简单多边形外,其他图形接下来是若干个(x,y)形式的字符串,其中x,y描述图形中的一个端点或顶点的位置,对与简单多边形,会在图形类别后输入一个正整数,代表总边数,其后是若干个(x,y),每组数据到某行第一个字符为’-‘结束,输入到某行第一个字符为’.’结束
限制
1<=图形数<=26;3<=简单多边形边数<=20;0<=坐标绝对值<=10000
输出
每行一个字符串,若某图形与任何图形不相交,则为
A has no intersections
若与一个图形相交,则为
A intersects with B
若与两个图形相交,则为
A intersects with B and C
若与三个及以上图形相交,则(以三个为例)为
A intersects with B, C, and D
其中A均为该图形名称,其他大写字母为与之相交图形名称,输出的每行按该图形名称字典序排列,每行内与之相交的图形名称也要按字典序排列,两组输出中间有一空行
样例输入
A square (1,2) (3,2)
F line (1,3) (4,4)
W triangle (3,5) (5,5) (4,3)
X triangle (7,2) (7,4) (5,3)
S polygon 6 (9,3) (10,3) (10,4) (8,4) (8,1) (10,2)
B rectangle (3,3) (7,5) (8,3)
-
B square (1,1) (2,2)
A square (3,3) (4,4)
-
.
样例输出
A has no intersections
B intersects with S, W, and X
F intersects with W
S intersects with B
W intersects with B and F
X intersects with BA has no intersections
B has no intersections
讨论
计算几何,题目本身不难,因为内部不算(否则求线段是否在多边形中又是一串代码),所以只要枚举所有线段,有相交的就行,比较烦人的在于这个输入输出的格式,不过也只是考察基本代码力而已
通篇代码几乎没有上stl的必要,数据规模也不算大,如果硬开map(记名称)和multimap(记相交),由于常数巨大,运行用时会上升到500ms
输入,处理,输出三部分的结束位置都已标注,方便划分阶段
题解状态
204K,79MS,C++,4160B
题解代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<map>
using namespace std;
#define INF 0x3f3f3f3f
#define MAXN 520
#define memset0(a) memset(a,0,sizeof(a))
#define EPS 1e-6
int nums[26];//每个图形的边数 0为该图形不存在
double x[MAXN], y[MAXN];//所有点的坐标 520=26*20
bool inter[26][26], f;//intersected 两个下标所指代的图形是否相交 flag 控制空行输出
double xp(double x1, double y1, double x2, double y2, double x3, double y3)
{
return (x1 - x2)*(y3 - y2) - (y1 - y2)*(x3 - x2);
}
bool onsegment(double x, double y, double x1, double y1, double x2, double y2)
{
return min(x1, x2) <= x&&x <= max(x1, x2) && min(y1, y2) <= y&&y <= max(y1, y2);
}
bool intersect(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4)
{
double xp1 = xp(x3, y3, x1, y1, x2, y2);
double xp2 = xp(x4, y4, x1, y1, x2, y2);
double xp3 = xp(x1, y1, x3, y3, x4, y4);
double xp4 = xp(x2, y2, x3, y3, x4, y4);
if (xp1*xp2 < 0 && xp3*xp4 < 0)
return 1;
else if (abs(xp1) < EPS&&onsegment(x3, y3, x1, y1, x2, y2))
return 1;
else if (abs(xp2) < EPS&&onsegment(x4, y4, x1, y1, x2, y2))
return 1;
else if (abs(xp3) < EPS&&onsegment(x1, y1, x3, y3, x4, y4))
return 1;
else if (abs(xp4) < EPS&&onsegment(x2, y2, x3, y3, x4, y4))
return 1;
return 0;
}//三大函数 天天用完全不想多介绍
void fun()
{
char name[10];//同时存储图形名称和类别的临时字符串
while (scanf("%s", name) && name[0] != '-') {//input
int num = name[0] - 'A';//将图形名称转换为数字 方便处理和输出
scanf("%s", name);//input
if (name[0] == 's') {//正方形
double x0, y0, x1, y1, x2, y2, x3, y3;
nums[num] = 4;//4个顶点
scanf(" (%lf,%lf) (%lf,%lf)", &x0, &y0, &x2, &y2);//input
x1 = (x0 + x2 + y2 - y0) / 2;
x3 = (x0 + x2 + y0 - y2) / 2;
y1 = (y0 + y2 + x0 - x2) / 2;
y3 = (y0 + y2 - x0 + x2) / 2;//求正方形另外两个点的公式 只要列出中点坐标公式 一条对角线端点横坐标差等于另一条对角线端点纵坐标差 联立即可得出上述公式
x[num * 20] = x0, y[num * 20] = y0;
x[num * 20 + 1] = x1, y[num * 20 + 1] = y1;
x[num * 20 + 2] = x2, y[num * 20 + 2] = y2;
x[num * 20 + 3] = x3, y[num * 20 + 3] = y3;//num是图形名称与'A'的差 每个图形单独占据20个点的位置 先计算后赋值以避免反复计算元素位置
}
else if (name[0] == 'r') {//矩形
nums[num] = 4;//4个顶点
scanf(" (%lf,%lf) (%lf,%lf) (%lf,%lf)", &x[num * 20], &y[num * 20], &x[num * 20 + 1], &y[num * 20 + 1], &x[num * 20 + 2], &y[num * 20 + 2]);//input
x[num * 20 + 3] = x[num * 20] + x[num * 20 + 2] - x[num * 20 + 1];
y[num * 20 + 3] = y[num * 20] + y[num * 20 + 2] - y[num * 20 + 1];//利用中点坐标公式求得最后一个点的位置
}
else if (name[0] == 'l') {//线段
nums[num] = 2;//2个端点
scanf(" (%lf,%lf) (%lf,%lf)", &x[num * 20], &y[num * 20], &x[num * 20 + 1], &y[num * 20 + 1]);//input
}
else if (name[0] == 't') {//三角形
nums[num] = 3;//3个顶点
scanf(" (%lf,%lf) (%lf,%lf) (%lf,%lf)", &x[num * 20], &y[num * 20], &x[num * 20 + 1], &y[num * 20 + 1], &x[num * 20 + 2], &y[num * 20 + 2]);//input
}
else {//简单多边形
scanf("%d", &nums[num]);//input//读取边数
for (int p = 0; p < nums[num]; p++)
scanf(" (%lf,%lf)", &x[num * 20 + p], &y[num * 20 + p]);//input//读取点
}
}
//input ends here
for (int p = 0; p < 26; p++) {//枚举第一个图形
if (!nums[p])//图形不存在 直接略过
continue;
for (int o = p + 1; o < 26; o++) {//枚举第二个图形
if (!nums[o] || inter[p][o])//图形不存在或已经相交 略过
continue;
for (int i = 0; i < nums[p]; i++)//枚举第一个图形的边
for (int u = 0; u < nums[o]; u++)//枚举第二个图形的边
if (intersect(x[p * 20 + i], y[p * 20 + i], x[p * 20 + (i + 1) % nums[p]], y[p * 20 + (i + 1) % nums[p]], x[o * 20 + u], y[o * 20 + u], x[o * 20 + (u + 1) % nums[o]], y[o * 20 + (u + 1) % nums[o]])) {//判断相交
inter[p][o] = inter[o][p] = 1;//标记相交
goto bp1;//breakpoint 由于要跳出两层循环 故用goto
}
bp1:
;//没这个分号编译不通过
}
}
//process ends here
if (f)
printf("\n");//output
for (int p = 0; p < 26; p++) {
if (!nums[p])//不存在 略过
continue;
int cnt2 = 0;//与之相交的图形数
for (int o = 0; o < 26; o++)
cnt2 += inter[p][o];
if (!cnt2)//0个
printf("%c has no intersections\n", p + 'A');//output
else if (cnt2 == 1) {//1个
int o;
for (o = 0; !inter[p][o]; o++);
printf("%c intersects with %c\n", p + 'A', o + 'A');//output
}
else if (cnt2 == 2) {//2个
int o;
for (o = 0; !inter[p][o]; o++);
printf("%c intersects with %c", p + 'A', o + 'A');//output
for (o++; !inter[p][o]; o++);
printf(" and %c\n", o + 'A');//output
}
else {//不少于3个
int o = -1;//因为一开始就要+1 故初始化为-1
printf("%c intersects with", p + 'A');//output
while (--cnt2) {//先递减以少输出最后一个
for (o++; !inter[p][o]; o++);
printf(" %c,", o + 'A');//output
}
for (o++; !inter[p][o]; o++);
printf(" and %c\n", o + 'A');//output
}
}
f = 1;
//output ends here
}
int main(void)
{
//freopen("vs_cin.txt", "r", stdin);
//freopen("vs_cout.txt", "w", stdout);
char ch;//char 存放数据第一个字符
while (~scanf("%c", &ch) && ch != '.') {//input
ungetc(ch, stdin);//没事再退回去
fun();
memset0(nums);
memset0(inter);
getchar();//'-'后还有一个换行需要吞掉才到新的一行
}
}
EOF