题意:一个x*y的矩形里,给了n个圆,n不超过10,保证圆与圆只有相离或外切两种关系,要求选择一个点,这个点的任意一条射线,都最多只与一个圆相交。射线与圆相交指的是射线与圆有两个交点。
链接:
http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=162109 (VJ)
http://codeforces.com/gym/100345/ (CF J题)
解法:圆不多,暴力可解。枚举两两圆的公切线,打入一个集合L里,再打入矩形的四条边。枚举L里两两条线,求交点,打入一个集合P里,再将矩形四个点和每个切点打入。可以认为答案一定在集合P里,否则就无解。枚举集合P里每个点,再枚举每个圆,这个点和圆做三条线,即两条公切线和一条圆心到该点的连线,这三条线都当作射线对待,然后再枚举除了当前圆以外的所有圆,如果存在射线和圆相交(交点数>2),则当前点不能作为答案,否则可以。
小结:思维上不是太难,编码难度有点大,这里涉及到圆和圆求公切线,直线和圆求公切线等问题,都需要分情况讨论,写起来不那么简单,建议形成模板块。
本题代码
//Hello. I'm Peter.
//#pragma comment(linker, "/STACK:102400000,102400000")
#include<cstdio>
#include<iostream>
#include<sstream>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<functional>
#include<cctype>
#include<ctime>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;
#define peter cout<<"i am peter"<<endl
#define fuck(x) cerr << #x << " <- " << x << endl
typedef long long ll;
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
/*--------------------------定量和定函数--------------------------*/
const double eps = 1e-9, pi = acos(-1.0);
inline const int sgn(double x){
if(fabs(x) < eps) return 0;
else return x > 0? 1 : -1;
}
inline const double sq(double x){return x*x;}
/*--------------------------向量--------------------------*/
struct Point{
double x, y;
Point(){};
Point(double x1, double y1){x = x1, y = y1;}
};
typedef Point Vector;
Vector operator + (const Vector a, const Vector b){
return Vector(a.x + b.x, a.y + b.y);
}
Vector operator - (const Vector a, const Vector b){
return Vector(a.x - b.x, a.y - b.y);
}
double operator * (const Vector a, const Vector b){
return a.x * b.x + a.y * b.y;
}
double operator % (const Vector a, const Vector b){
return a.x * b.y - a.y * b.x;
}
Vector operator * (const Vector a, const double b){
return Vector(a.x * b, a.y * b);
}
Vector operator * (const double b, const Vector a){
return Vector(a.x * b, a.y * b);
}
Vector operator / (const Vector a, const double b){
return Vector(a.x / b, a.y / b);
}
bool operator == (const Point a, const Point b){
return sgn(a.x - b.x)==0 && sgn(a.y - b.y)==0;
}
bool operator || (const Vector a, const Vector b){
return sgn(a % b)==0;
}
bool operator / (const Vector a, const Vector b){
return sgn(a % b)!=0;
}
double Length(Vector v){
return (double)sqrt((double)(v.x * v.x + v.y * v.y));
}
double LenSq(Vector v){
return v.x*v.x + v.y*v.y;
}
double Dis(Point a, Point b){
return Length(a - b);
}
Vector Rotate(Vector v, double rad){
return Vector(v.x * cos(rad) - v.y * sin(rad), v.x * sin(rad) + v.y * cos(rad));
}
Vector Norv(Vector v){
return Vector(v.y, -v.x);
}
Vector Unitv(Vector v){
return v / Length(v);
}
double angle(Vector v){
return atan2(v.y, v.x);
}
double angle(Vector a, Vector b){
double ans = angle(a) - angle(b);
while(sgn(ans) < 0) ans += 2*pi; while(sgn(ans) >= 2*pi) ans -= 2*pi;
return fmin(ans, 2*pi - ans);
}
//以下是排序和去重使用的比较函数
bool cmpxy(const Point a, const Point b){
if(sgn(a.x-b.x)) return a.x < b.x;
else return a.y < b.y;
}
bool cmpeq(const Point a, const Point b){
return a==b;
}
//以上是排序和去重使用的比较函数
/*--------------------------直线--------------------------*/
struct Line{
Point p; Vector v;
Line(){};
Line(Point p1, Vector v1){p = p1, v = v1;}
};
Point operator / (const Line a, const Line b){
double t = ((b.p - a.p) % b.v) / (a.v % b.v);
return a.p + a.v * t;
}
double Dis(Point p, Line l){
return fabs(l.v % (p - l.p)) / Length(l.v);
}
/*--------------------------射线--------------------------*/
typedef Line Ray;
/*--------------------------圆--------------------------*/
struct Circle{
Point p;
double r;
Circle(){};
Circle(Point p1, double r1){p = p1, r = r1;}
Point point(double rad){
return Point(p.x + r * cos(rad), p.y + r * sin(rad));
}
};
//以下几个位置关系函数,有些题目重新书写会更好
bool ExTan (const Circle c1, const Circle c2){//外切
return sgn(Dis(c1.p, c2.p) - (c1.r + c2.r)) == 0;
}
//以上几个位置关系函数,有些题目重新书写会更好
bool PointOnCircle(Point p, Circle c){
return sgn(Dis(p, c.p) - c.r) == 0;
}
bool PointInCircle(Point p, Circle c){
return sgn(Dis(p, c.p) - c.r) < 0;
}
void Tan_Circles_sub1(Circle c1, Circle c2, Line *l, int &nl){
//第一种情况,求两圆两条外切线,前提是两圆相离或外切或相交
//修改..
if(c1.r > c2.r) swap(c1, c2);//保证c1较小
Vector v12 = c2.p - c1.p;
double av = angle(v12);
double b = c2.r - c1.r;
double c = Dis(c1.p, c2.p);
double a = acos(b/c);
Point x11, x12, x21, x22;
x11 = c1.point(av + a), x12 = c1.point(av - a);
x21 = c2.point(av + a), x22 = c2.point(av - a);
l[nl++] = Line(x11, x21 - x11), l[nl++] = Line(x12, x22 - x12);
}
void Tan_Circles_sub2(Circle c1, Circle c2, Line *l, int &nl){
//第二种情况,求两圆两条内切线,前提是两圆相离
//修改..
double dis = Dis(c1.p, c2.p);
double d2 = dis / ((c1.r/c2.r) + 1);
double d1 = dis - d2;
double a1 = acos(c1.r / d1);
Vector v12 = c2.p - c1.p;
double av12 = angle(v12);
Point x11, x12;
x11 = c1.point(av12 + a1), x12 = c1.point(av12 - a1);
double a2 = acos(c2.r / d2);
Vector v21 = c1.p - c2.p;
double av21 = angle(v21);
Point x21, x22;
x21 = c2.point(av21 + a2), x22 = c2.point(av21 - a2);
l[nl++] = Line(x11, x21 - x11), l[nl++] = Line(x12, x22 - x12);
}
void Tan_Circles_sub3(Circle c1, Circle c2, Line *l, int &nl){
//第三种情况,两圆外切时,求一条内切线
if(!ExTan(c1, c2)) return;
Vector v12 = c2.p - c1.p;
Point x = c1.p + (Unitv(v12)*(c1.r));
Vector norv = Norv(v12);
l[nl++] = Line(x, norv);
}
void Tan_Circles(Circle c1, Circle c2, Line *l, int &nl){
//修改
//nl = 0;//这里经常需要修改,特别注意
Tan_Circles_sub1(c1, c2, l, nl);
Tan_Circles_sub2(c1, c2, l, nl);
Tan_Circles_sub3(c1, c2, l, nl);
//修改
}
/*--------------------------不同类相交--------------------------*/
bool LineInterCircle(Line l, Circle c){
return sgn(Dis(c.p, l) - c.r) < 0;
}
bool RayInterCircle(Line l, Circle c){
return LineInterCircle(l, c) && sgn((c.p - l.p) * l.v) >0;
}
void Tan_PointCircle(Point p, Circle c, Line *l, int &nl){
//求一个点到一个圆的两条切线,前提是该点不能在圆内
if(PointInCircle(p, c)) return;
nl = 0;//这里经常需要修改,特别注意
if(PointOnCircle(p, c)){
Vector v = Norv(p - c.p);
l[nl++] = Line(p, v);
//当求的是射线时,这里需要修改,特别注意
//修改
l[nl++] = Line(p, v * (-1));
l[nl++] = Line(p, c.p - p);
return;
}
else{
double a = asin(c.r / Dis(p, c.p));
Vector v;
v = Rotate(c.p - p, a);
l[nl++] = Line(p, v);
v = Rotate(c.p - p, -a);
l[nl++] = Line(p, v);
//修改
l[nl++] = Line(p, c.p - p);
}
}
/*--------------------------读入--------------------------*/
Point readPoi(){
int x, y;
x = read(), y = read();
//确保读入为int整形
return Point(x, y);
}
Circle readCir(){
Point p = readPoi();
int r; r = read();
//确保读入为int整形
return Circle(p, r);
}
/*----------------------------------------------------*/
#define N 100010
int n,xx,yy;
Point p[N];
Line l[N];
Circle c[N];
int nump, numl;
bool inrange(double x, double a, double b){
return sgn(a-x)<=0 && sgn(x-b)<=0;
}
bool inMaze(Point p){
return inrange(p.x, 0, xx) && inrange(p.y, 0, yy);
}
int main(){
freopen("zen.in","r",stdin);
freopen("zen.out","w",stdout);
cin>>n>>xx>>yy;
for(int i = 0; i < n; i++) c[i] = readCir();
nump = numl = 0;
p[nump++] = Point(0, 0), p[nump++] = Point(xx, 0), p[nump++] = Point(xx, yy), p[nump++] = Point(0, yy);
for(int i = 0; i < nump; i++){
int nex = i == nump-1? 0: i +1;
l[numl++] = Line(p[i], p[nex] - p[i]);
}
for(int i = 0; i < n; i++){
for(int j = i + 1; j < n; j++){
int lastn;
lastn = numl;
Tan_Circles(c[i], c[j], l, numl);
for(int k = lastn; k < numl; k++){
p[nump++] = l[k].p;
}
lastn = numl;
Tan_Circles(c[j], c[i], l, numl);
for(int k = lastn; k < numl; k++){
p[nump++] = l[k].p;
}
numl = lastn;
}
}
for(int i = 0; i < numl; i++){
for(int j = i + 1; j < numl; j++){
if(l[i].v / l[j].v) p[nump++] = l[i] / l[j];
}
}
sort(p, p + nump, cmpxy);
nump = (int)(unique(p, p + nump, cmpeq) - p);
for(int i = 0; i < nump; i++){
if(!inMaze(p[i])) continue;
bool ok = true;
for(int j = 0; j < n && ok; j++){
if(PointInCircle(p[i], c[j])) ok = false;
}
for(int j = 0; j < n && ok; j++){
Line l1[10]; int numl1;
Tan_PointCircle(p[i], c[j], l1, numl1);
for(int a = 0; a < numl1 && ok; a++){
for(int k = 0; k < n && ok; k++){
if(j == k) continue;
if(RayInterCircle(l1[a], c[k])) ok = false;
}
}
}
if(ok){
printf("%.10f %.10f\n", p[i].x, p[i].y);
return 0;
}
}
printf("No Zen\n");
return 0;
}