[Offer收割]程式設計練習賽2 hihocoder 1275 掃地機器人 (計算幾何+模擬 比較煩)

_TCgogogo_發表於2016-03-14
時間限制: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軸正方向為向右):


樣例3(x軸正方向為向下,y軸正方向為向右):

樣例輸入
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表示左轉,由於回到起點時起點也要判斷,因此把第二個點作為最後一個點。由於我們是按照輸入順序即順時針方向貼邊內側走,因此可以發現,每次到一個角時,如果是向右轉則必定在內角,否則在外角,如下圖:

之後對於每個角算出機器人所要佔據的面積再列舉每一條線,判斷是否有線穿過機器人,有則無解,不衝突的情況共8種,如下圖:


開始用了騙分的那種思路做特判其實也是不對的,比如這種圖形


短邊為1,長邊為2,顯然用邊長為2的機器人是可以全部打掃到的

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#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");
        }
    }
}


相關文章