你是一个圆,然后平面还有n个圆,问你能否通过移动自己,突出重围!再抽象一点,就是判断一个圆,是否在其他若干个彼此相连的圆的“大圈圈”内。。。
这里不得不强烈推荐watashi大神的神思路。。。神奇地将此题转化成图论模型!
个人的理解是,如果某两个圆相交或相切,那么他们组成的封闭区间可以用连接其两圆心的线段表示,这样能得到若干线段,这样就成了,若干线段中,是否能组成一个多边形,使得起始圆心在多边形内呢?
然后神思路就来了。。。判断一个点是否在多边形内,如果按顺时针扫描每一条边,依次计算经过该边的跨度(或者说仰视角),如果在点内,和为2PI,如果逆时针来,就是-2PI。 如果在多边形外的话,跨度和必然是0.于是就能根据这个性质建图喽~!以每个圆心为节点,两两相交的圆,根据两个圆心的顺/逆时针顺序,添加两条边,一个ang,一个-ang。然后就抽象成了是否存在一个负环。。。Orz。。。
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
#include<queue>
#define FF(i, a, b) for(int i=a; i<b; i++)
#define FD(i, a, b) for(int i=a; i>=b; i--)
#define REP(i, n) for(int i=0; i<n; i++)
#define PB push_back
#define CLR(a, b) memset(a, b, sizeof(a))
using namespace std;
const int maxn = 333;
const double eps = 1e-8;
struct Edge
{
int to;
double dist;
Edge(int a=0, double b=0):to(a), dist(b){}
};
struct spfa
{
int n, m;
vector<int> G[maxn];
vector<Edge> edges;
int cnt[maxn];
double d[maxn];
bool inq[maxn];
void init(int n)
{
this->n = n;
REP(i, n) G[i].clear();
edges.clear();
}
void AddEdge(int from, int to, double dist)
{
edges.PB(Edge(to, dist));
m = edges.size();
G[from].PB(m-1);
}
bool NegativeCycle()
{
queue<int> q;
REP(i, n) d[i] = 0, inq[i] = true, cnt[i] = 0, q.push(i);
while(!q.empty())
{
int u = q.front(); q.pop();
inq[u] = false;
REP(i, G[u].size())
{
Edge e = edges[G[u][i]];
//不加eps是要wa的
if(d[e.to] > d[u] + e.dist + eps)
{
d[e.to] = d[u] + e.dist;
if(!inq[e.to])
{
q.push(e.to);
inq[e.to] = true;
if(++cnt[e.to] >= n) return true;
}
}
}
}
return false;
}
}solver;
int T, n;
double x[maxn], y[maxn], r[maxn];
template <class T> T sqr(T x) { return x*x; }
double dist(int i, int j)
{
return sqrt(sqr(x[i]-x[j]) + sqr(y[i]-y[j]));
}
int main()
{
scanf("%d", &T);
while(T--)
{
scanf("%d", &n);
solver.init(n);
REP(i, n) scanf("%lf%lf%lf", &x[i], &y[i], &r[i]);
scanf("%lf%lf%lf", &x[n], &y[n], &r[n]);
//强制回到点(0, 0)
REP(i, n) x[i] -= x[n], y[i] -= y[n], r[i] += r[n];
x[n] = y[n] = 0;
REP(i, n) FF(j, i+1, n)
{
//不加eps就wa到死
if(r[i] + r[j] - dist(i, j) < eps) continue;
//flag 看顺时针或逆时针
bool flag = (x[i]*y[j]-y[i]*x[j] >= 0);
//向量夹角
double ang = acos((x[i]*x[j]+y[i]*y[j]) / dist(i, n) / dist(j, n));
//正反两条边
solver.AddEdge(i, j, flag ? ang : -ang);
solver.AddEdge(j, i, flag ? -ang : ang);
}
printf("%s\n", solver.NegativeCycle() ? "NO" : "YES");
if(T) puts("");
}
return 0;
}