描述
小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
《扫地机器人》题目分析
这个题目是一道比较复杂的计算几何题目。
首先我们要想明白机器人能完整清扫的充分必要条件是什么?
我们分析一些情况之后,就会有一个直观的判断:如果机器人能贴着边界转一圈回到出发点,那么就可以完整清扫整个房间。反之,如果机器人在贴着边界移动过程中会受到阻碍,那么就没办法完整清扫整个房间。
对于判断机器人能否贴着边界转一圈这个问题,我们可以把它分解成若干个子问题:判断机器人能否贴着边界从一个转角移动到下一个转角。如果对于某次转角到转角的移动受到阻碍,那就转一圈也一定会受到阻碍;反之如果每次转角到转角都不受阻碍,那么转一圈也不会受到阻碍。
当机器人从一个转角移动到下一个转角时,机器人扫过的范围一定是一个矩形。当机器人在上述移动中遇到阻碍时,这个矩形一定与某段房间边界相交。
于是我们有了一个解决本题的思路:
1. 假设机器人顺时针转一圈,对于相邻的两个转角A和B,计算机器人从A到B时扫过的矩形位置。
2. 判断该矩形是否与某段房间边界相交。
对于第一个问题,我们可以考虑机器人开始和结束的位置,也就是机器人在A转角时的位置和机器人在B转角时的位置。这里位置是要求出机器人4个顶点的坐标。如果我们知道机器人分别处于转角A和转角B时的4个顶点坐标,那么这8个坐标中最左上的点就是扫过矩形的左上角,最右下的点就是扫过矩形的右下角。
于是问题进一步简化为:对于每一个转角A,求机器人在A时4个顶点的坐标。
首先我么把转角分为内角(顺时针转90度)和外角(顺时针转270度)。例如上图中蓝圈圈住的转角是内角,红圈圈住的转角是外角。其次对于一个转角A,我们可以将转角A视为由两条有向线段收尾相接形成的。我们把前一条有向线段的方向称为“入角方向”,后一条有向线段的方向称为“出角方向”。
如果A是内角,那么机器人一个顶点a一定在A点处,另外一个顶点b一定在a沿着出角方向移动M处,另外一个顶点c一定在a沿着入角方向反向移动M处。如果A是外角,同样机器人一个顶点a一定在A点处,另外一个顶点b一定在a沿着出角方向反向移动M处,另外一个顶点c一定在a沿着入角方向移动M处。
于是我们就可以求出机器人在一个转角时,4个顶点的位置。
第二个问题,是一个特殊的判断矩形与线段相交的问题。特殊在矩形的边和线段都是平行于坐标轴的。比较简单的方法是讨论什么情况下矩形与线段不相交。可以发现如果不相交,那么矩形与线段一定在X轴方向或Y轴方向上是相离的。
最后需要说明的一点是:有些判断方法是有漏洞的,比如只判断机器人在转角时是否与房间边界相交。你能想到反例吗?
代码:
//
// Created by liyuanshuo on 2017/3/31.
//
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
typedef long long ll;
ll sign( ll x )
{
if( x < 0 )
return -1ll;
if( x > 0 )
return 1ll;
return 0ll;
}
void get_ans( )
{
ll n, m;
cin>>n>>m;
vector<ll> x(n), y(n);
for (int i = 0; i <n ; ++i)
{
cin>>x[i]>>y[i];
}
x.push_back (x[0]), x.push_back (x[1]);
y.push_back (y[0]), y.push_back (y[1]);
n = x.size ();
bool flag = true;
for (int i = 1; i < n-1 ; ++i)
{
ll xx[3], yy[3];
xx[0] = x[i-1];
xx[1] = x[i];
xx[2] = x[i+1];
yy[0] = y[i-1];
yy[1] = y[i];
yy[2] = y[i+1];
ll a, b, c, d;
a = xx[1] - xx[0];
b = yy[1] - yy[0];
c = xx[2] - xx[1];
d = yy[2] - yy[1];
if( (a*d - b*c) < 0ll )
{
xx[0] = xx[1] + sign (xx[0] - xx[1]) * m;
yy[0] = yy[1] + sign (yy[0] - yy[1]) * m;
xx[2] = xx[1] + sign (xx[2] - xx[1]) * m;
yy[2] = yy[1] + sign (yy[2] - yy[1]) * m;
}
else
{
xx[0] = xx[1] - sign (xx[0] - xx[1]) * m;
yy[0] = yy[1] - sign (yy[0] - yy[1]) * m;
xx[2] = xx[1] - sign (xx[2] - xx[1]) * m;
yy[2] = yy[1] - sign (yy[2] - yy[1]) * m;
}
a = min (xx[0], xx[2]);
b = max (xx[0], xx[2]);
c = min (yy[0], yy[2]);
d = max (yy[0], yy[2]);
for (int j = 0; j <n-1 ; ++j)
{
xx[0] = x[j], xx[1] = x[j+1];
yy[0] = y[j], yy[1] = y[j+1];
if( a >= max (xx[0], xx[1]) || b <= min (xx[0], xx[1]) || c >= max (yy[0], yy[1]) || d <= min (yy[0], yy[1]) )
continue;
flag = false;
if( !flag )
break;
}
if ( !flag )
break;
}
if ( flag )
cout<<"Yes"<<endl;
else
cout<<"No"<<endl;
}
int main( )
{
//freopen ("F:\\CSLeaning\\Thinking in C++\\hihocoder\\in.in", "r", stdin);
int m;
cin>>m;
while ( m-- )
{
get_ans ();
}
return 0;
}