hiho一下 第142周 掃地機器人

時間限制:
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軸正方向爲向右):

《hiho一下 第142周 掃地機器人》

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

《hiho一下 第142周 掃地機器人》

樣例輸入

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

《掃地機器人》題目分析

這個題目是一道比較複雜的計算幾何題目。

首先我們要想明白機器人能完整清掃的充分必要條件是什麼?

我們分析一些情況之後,就會有一個直觀的判斷:如果機器人能貼着邊界轉一圈回到出發點,那麼就可以完整清掃整個房間。反之,如果機器人在貼着邊界移動過程中會受到阻礙,那麼就沒辦法完整清掃整個房間。

《hiho一下 第142周 掃地機器人》

對於判斷機器人能否貼着邊界轉一圈這個問題,我們可以把它分解成若干個子問題:判斷機器人能否貼着邊界從一個轉角移動到下一個轉角。如果對於某次轉角到轉角的移動受到阻礙,那就轉一圈也一定會受到阻礙;反之如果每次轉角到轉角都不受阻礙,那麼轉一圈也不會受到阻礙。

當機器人從一個轉角移動到下一個轉角時,機器人掃過的範圍一定是一個矩形。當機器人在上述移動中遇到阻礙時,這個矩形一定與某段房間邊界相交。

《hiho一下 第142周 掃地機器人》

於是我們有了一個解決本題的思路: 
1. 假設機器人順時針轉一圈,對於相鄰的兩個轉角A和B,計算機器人從A到B時掃過的矩形位置。 
2. 判斷該矩形是否與某段房間邊界相交。

對於第一個問題,我們可以考慮機器人開始和結束的位置,也就是機器人在A轉角時的位置和機器人在B轉角時的位置。這裏位置是要求出機器人4個頂點的座標。如果我們知道機器人分別處於轉角A和轉角B時的4個頂點座標,那麼這8個座標中最左上的點就是掃過矩形的左上角,最右下的點就是掃過矩形的右下角。

於是問題進一步簡化爲:對於每一個轉角A,求機器人在A時4個頂點的座標。

《hiho一下 第142周 掃地機器人》

首先我麼把轉角分爲內角(順時針轉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軸方向上是相離的。

《hiho一下 第142周 掃地機器人》

最後需要說明的一點是:有些判斷方法是有漏洞的,比如只判斷機器人在轉角時是否與房間邊界相交。你能想到反例嗎?

代碼:
//
// 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;
}

点赞