[Offer收割]编程练习赛2 hihocoder 1275 扫地机器人 (计算几何+模拟 比较烦)

时间限制:10000ms
单点时限:1000ms
内存限制:256MB

描述

小Ho最近买了一台扫地机器人用来代替他清扫实验室的卫生,扫地机器人有不同的尺寸,但是通常来说可以被视作一个M*M的正方形,扫地机器人仅能清扫被自己覆盖过的区域。

小Ho所在的实验室是一个多边形,任意两条边之间要么为垂直关系要么为平行关系。扫地机器人也仅能沿着这两个方向平移,不能旋转。实验室中的一些区域过于狭窄,所以对扫地机器人的大小就有了限制。

于是小Ho找到了你,给出实验室的地形和扫地机器人的大小,希望你能够判断给定的扫地机器人能否成功清扫实验室的每一块区域。

输入

每个输入文件包含多组测试数据,在每个输入文件的第一行为一个整数Q,表示测试数据的组数。

每组测试数据的第一行为两个正整数N和M,分别表示多边形的点数和机器人的大小。

接下来的N行,每行为两个整数X、Y,表示多边形的一个顶点。

多边形的顶点按照“顺时针”顺序给出,即从当前点前往下一个点时,多边形的“内部”在右侧方向,多边形的边均平行于坐标轴

对于20%的数据,满足0<=N<=200,1<=X、Y、M<=100

对于100%的数据,满足0<=N<=1000,1<=X、Y、M<=108

对于100%的数据,满足实验室可以由一个1*1的扫地机器人完成清扫。

对于100%的数据,满足Q<=5

输出

对于每组测试数据,如果机器人能够顺利完成任务,输出Yes,否则输出No。

样例提示

样例1(x轴正方向为向下,y轴正方向为向右):

[Offer收割]编程练习赛2 hihocoder 1275 扫地机器人 (计算几何+模拟 比较烦)_第1张图片

样例3(x轴正方向为向下,y轴正方向为向右):

[Offer收割]编程练习赛2 hihocoder 1275 扫地机器人 (计算几何+模拟 比较烦)_第2张图片

样例输入
3
6 2
0 0
0 2
2 2
2 3
3 3
3 0
6 2
0 0
0 3
3 3
3 5
5 5
5 0
8 2
0 0
0 2
1 2
1 3
3 3
3 1
2 1
2 0
样例输出
No
Yes
No

题目链接:http://hihocoder.com/problemset/problem/1275

题目分析:首先先%一下比赛时的满分选手,再吐槽一下直接找边平方最小与m方判断都能得70分的数据。。。

这题做了好久。。。中间过程比较烦,这题的大体思路是模拟,即从起点开始按输入顺序(顺时针)让机器人沿着多边形的内边走,每走一条边判断一下首先要处理知道一条边和下一个点怎么判断往哪个方向转的问题,先设三个点p1(当前线的起点),p2(当前线的终点),p3(下一个点)为了求方向我们把它们定义为空间中的三点p1(x1, y1, 0),p2(x2, y2, 0),p3(x3, y3, 0)

那么向量p1->p2表示为α=(x2-x1, y2-y1, 0),向量p2->p3表示为β=(x3-x2, y3-y2, 0),α×β (×为叉乘)得到f = (x1-x3)*(y2-y3)-(x2-x3)*(y1-y3)

根据右手定则,f>0时右转,f<0时左转。因此我们可以先算出机器人绕多边形一圈的整个转向过程,1表示右转,0表示左转,由于回到起点时起点也要判断,因此把第二个点作为最后一个点。由于我们是按照输入顺序即顺时针方向贴边内侧走,因此可以发现,每次到一个角时,如果是向右转则必定在内角,否则在外角,如下图:
[Offer收割]编程练习赛2 hihocoder 1275 扫地机器人 (计算几何+模拟 比较烦)_第3张图片

之后对于每个角算出机器人所要占据的面积再枚举每一条线,判断是否有线穿过机器人,有则无解,不冲突的情况共8种,如下图:

[Offer收割]编程练习赛2 hihocoder 1275 扫地机器人 (计算几何+模拟 比较烦)_第4张图片

开始用了骗分的那种思路做特判其实也是不对的,比如这种图形

[Offer收割]编程练习赛2 hihocoder 1275 扫地机器人 (计算几何+模拟 比较烦)_第5张图片

短边为1,长边为2,显然用边长为2的机器人是可以全部打扫到的

#include 
#include 
#include 
#include 
#include 
#define ll long long
using namespace std;
int const MAX = 1e3 + 5;
int n, m;
int dir[MAX]; //0左,1右

struct POINT
{
    int x, y;
}p[MAX];

struct LINE
{
    POINT s, e;
}l[MAX];

struct ROBOT
{
    int x1, y1, x2, y2;
};

int Dir(POINT p1, POINT p2, POINT p3) // 1:右
{
    if((ll)(p1.x - p3.x) * (p2.y - p3.y) < (ll)(p2.x - p3.x) * (p1.y - p3.y))
        return 1;
    return 0;
}

bool OK(ROBOT r, LINE a)
{
    if(a.s.x < 0 || a.s.y < 0 || a.e.x < 0 || a.e.y < 0)
        return true;
    int min_x = min(a.s.x, a.e.x);
    int max_x = max(a.s.x, a.e.x);
    int min_y = min(a.s.y, a.e.y);
    int max_y = max(a.s.y, a.e.y);
    if(a.s.x == a.e.x)
        if((a.s.x <= r.x1 && a.s.x <= r.x2) || (a.s.x >= r.x1 && a.s.x >= r.x2) || max_y <= r.y1 || min_y >= r.y2)
            return false;
    if(a.s.y == a.e.y)
        if((a.s.y <= r.y1 && a.s.y <= r.y2) || (a.s.y >= r.y1 && a.s.y >= r.y2) || max_x <= r.x1 || min_x >= r.x2)
            return false;
    return true;
}

int main()
{
    int T;
    while(scanf("%d", &T) != EOF)
    {
        while(T --)
        {   
            bool flag = true;
            scanf("%d %d", &n, &m);
            for(int i = 1; i <= n; i++)
                scanf("%d %d", &p[i].x, &p[i].y);
            p[n + 1] = p[1];
            p[n + 2] = p[2];
            for(int i = 1; i <= n && flag; i++)
            {
                l[i].s = p[i];
                l[i].e = p[i + 1];
            }
            for(int i = 1; i <= n; i++)
                dir[i] = Dir(l[i].s, l[i].e, p[i + 2]);
            for(int i = 1; i <= n && flag; i++)
            {   
                ROBOT a;
                a.x1 = a.x2 = l[i].e.x;
                a.y1 = a.y2 = l[i].e.y;
                int dx = l[i].e.x - l[i].s.x;
                int dy = l[i].e.y - l[i].s.y;
                if(dir[i] == 1) //内角向右
                {
                    if(dx > 0)
                    {
                        a.x1 -= m;
                        a.y1 -= m; 
                    }
                    if(dx < 0)
                    {
                        a.x2 += m;
                        a.y2 += m;
                    }
                    if(dy > 0)
                    {
                        a.x2 += m;
                        a.y1 -= m;   
                    }
                    if(dy < 0)
                    {   
                        a.x1 -= m;
                        a.y2 += m;
                    }
                }
                else
                {
                    if(dx > 0)  //外角向左
                    {
                        a.x2 += m;
                        a.y1 -= m; 
                    }
                    if(dx < 0)
                    {
                        a.x1 -= m;
                        a.y2 += m;
                    }
                    if(dy > 0)
                    {
                        a.x2 += m;
                        a.y2 += m;
                    }
                    if(dy < 0)
                    {   
                        a.x1 -= m;
                        a.y1 -= m;
                    }
                }
                for(int i = 1; i <= n && flag; i++)
                    if(OK(a, l[i])) //true表示有交叉
                        flag = false;   
            }
            printf("%s\n", flag ? "Yes" : "No");
        }
    }
}


你可能感兴趣的:(模拟,计算几何,ACM)