H-Tiny Room(几何)
(一)题面:
Problem description |
You are an employee of Automatic Cleaning Machine (ACM) and a member of the development team of Intelligent Circular Perfect Cleaner (ICPC). ICPC is a robot that cleans up the dust of the place which it passed through. Your task is an inspection of ICPC. This inspection is performed by checking whether the center of ICPC reaches all the N given points. However, since the laboratory is small, it may be impossible to place all the points in the laboratory so that the entire body of ICPC is contained in the laboratory during the inspection. The laboratory is a rectangle of H×Wand ICPC is a circle of radius R. You decided to write a program to check whether you can place all the points in the laboratory by rotating and/or translating them while maintaining the distance between arbitrary two points. |
Input |
The input consists of a single test case of the following format. N H W R x1 y1 ... xN yN The first line consists of four integers N,H ,W ,R and (1≤N≤100 ,1≤H,W≤109, 1≤R≤106). The following N lines represent the coordinates of the points which the center of ICPC must reach. The (i+1)-th line consists of two integers xi and yi(0≤xi,yi≤109).xi and yi represent the x and y coordinates of the i-th point, respectively. It is guaranteed that the answer will not change even if R changes by 1. |
Output |
If all the points can be placed in the laboratory, print 'Yes'. Otherwise, print 'No'. |
Sample Input |
Sample Input 1 4 20 20 1 10 0 20 10 10 20 0 10 Sample Input 2 2 5 55 1 0 0 30 40 Sample Input 3 2 5 49 1 0 0 30 40 Sample Input 4 1 3 3 1 114 514 |
Sample Output |
Output for Sample Input 1 Yes Output for Sample Input 2 Yes Output for Sample Input 3 No Output for Sample Input 4 Yes |
(二)题意:
给定n个圆的圆心坐标和半径R(所有的圆的半径相等),以及一个规模为H×W的矩形,问是否能将所有的圆在不改变其相对位置的前提下(可以理解为将所有圆看成一个整体),通过旋转或者平移装进矩形。
(三)题解:
如果一个H×W的矩形能够装下所有的圆,那么一个(H-2R)×(W-2R)的矩形就一定能够装下所有的圆心,反过来也成立。故我们只要考虑将所有圆的圆心装进一个(H-2R)×(W-2R)的矩形。
(下面讨论假设W>=H,H=H-2R,W=W-2R)
①若存在某两点的距离大于sqrt(H*H+W*W),那么肯定无解。
①如果任意两个圆心之间的距离均小于H,那么这些点一定可以装进矩形(证明略)。
②如果这些圆心能够装入矩形,且存在某两个点,这两个点的距离大于等于H,那么我们通过变换点的位置以后,一定能让某条长度大于H的线段的两个端点分别落在矩形的两条平行边上,且所有的点仍然在矩形内。证明如下:
首先我们一定能够让某一点存在于矩形的一边上,而对于其余的点,若存在点位于对边上则命题成立。若当前不存在,那么我们一定能够得到下图所示的情况(假设AB>H,矩形中的凸多边形为最外围的点):
即有两个点分别在相邻边上,此时,我们绕着A点旋转凸多边形,因为存在长度大于H的对角线(或者边长),那么在旋转的过程中必定会有点碰到对边(如果不能旋转了,要么左右两边卡住了,要么上下两边卡住了),故命题成立。
(上述只是一个不很严谨的说明罢了)
③有了上述性质,接下来我们枚举所有圆心中圆心距大于H的点对,这两点分别转到矩形的两边上,对于其余的所有圆心均旋转一个相等的角度。然后判断一下其余的圆心是否全部位于两边之间(叉乘判断)且左右最远的点的横坐标之差(记录坐标)是否小于等于W,如果满足则可以将所有圆心装进矩形,如果对于任意上述的AB点对进行判断后都不能满足条件则无解。
④对于H>=W的情况也是一样的处理。
⑤复杂度:枚举点对O(N^2),判断其余的点合法性O(N),总复杂度O(N^3)。
(四)代码:
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<algorithm>
#define esp 1e-10
#define PI acos(-1)
using namespace std;
struct Point{
double x,y;
Point(double _x=0,double _y=0):x(_x),y(_y){}
};
typedef Point Vector;
Vector operator + (Vector A,Vector B){
return Vector(A.x+B.x,A.y+B.y);
}
Vector operator - (Vector A,Vector B){
return Vector(A.x-B.x,A.y-B.y);
}
Vector operator * (Vector A,double d){
return Vector(A.x*d,A.y*d);
}
Vector operator / (Vector A,double d){
return Vector(A.x/d,A.y/d);
}
bool operator < (const Point &A,const Point &B){
return A.x<B.x||(A.x==B.x&&A.y<B.y);
}
int dcmp(double x){
if(fabs(x)<esp)return 0;
return x<0?-1:1;
}
bool operator == (const Point &A,const Point &B){
return dcmp(A.x-B.x)==0&&dcmp(A.y-B.y)==0;
}
double Dot(Vector A,Vector B){ //点乘
return (A.x*B.x+A.y*B.y);
}
double Length(Vector A){ //向量的长度
return sqrt(Dot(A,A));
}
double angle(Vector v){ //向量的极角:从x轴正方向转到该向量的弧度,逆时针-正,顺时针-负
return atan2(v.y,v.x);
}
double Cross(Vector A,Vector B){ //叉乘
return (A.x*B.y-A.y*B.x);
}
Vector Rotate(Vector A,double rad){ //向量逆时针旋转rad弧度
return Vector(A.x*cos(rad)-A.y*sin(rad),A.x*sin(rad)+A.y*cos(rad));
}
const int maxn=105;
int N;
double H,W,R;
Point P[maxn];
bool Check(Point P1,Point P2,double limit,double rad){
//两点P1、P2,所有点绕P1顺时针旋转rad弧度
Point C=P1+Vector(1,0);
P2=P1+Rotate(P2-P1,-rad);
double _max=max(P1.x,P2.x),_min=min(P1.x,P2.x);
for(int i=0;i<N;i++){
Point p=P1+Rotate(P[i]-P1,-rad);
if(p==P1||p==P2)continue;
if(Cross(C-P1,p-P1)<0)return 0;
if(Cross(p-P2,C-P1)<0)return 0;
_min=min(_min,p.x);
_max=max(_max,p.x);
}
return _max-_min<=limit;
}
bool solve(){
double d,_maxd=0,_maxlimit=sqrt(W*W+H*H);
for(int i=0;i<N;i++){
for(int j=0;j<N;j++)if(i!=j){
_maxd=max(d=Length(P[i]-P[j]),_maxd);
if(_maxd>_maxlimit)return 0;
double rad=angle(P[j]-P[i]);
if(H<=d&&Check(P[i],P[j],W,rad-asin(H/d)))return 1;
if(W<=d&&Check(P[i],P[j],H,rad-asin(W/d)))return 1;
}
}
return _maxd<=min(H,W);
}
int main(){
while(~scanf("%d%lf%lf%lf",&N,&H,&W,&R)){
H-=2*R;W-=2*R;
for(int i=0;i<N;i++)scanf("%lf%lf",&P[i].x,&P[i].y);
printf((H>0&&W>0)&&(N==1||solve())?"Yes\n":"No\n");
}
return 0;
}
(五)总结:
开始以为还可以先求凸包来优化一下,实际上也是可以通过求凸包来优化,但是并不是我这种直接枚举点确定旋转角度的做法了(学姐提醒),因为只算凸包的话,有些角度可能枚举不到了。
计算几何,注意细节。