poj 3449

题目概述

平面坐标系内有若干个图形,可能的种类为正方形(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 B

A 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值