zoj2318 getout(计算几何)

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1318

题意:

你是一个圆,二维坐标中有很多其他n个圆,给出n个圆心及半径,再给你自己的圆心及半径,问这个圆能不能逃出这n个圆的包围。

tip:

好神奇的题。。首先考虑把自己这个圆变成点,把别的圆的半径加上自己这个半径就好了。如果某两个圆相交或相切,那么他们组成的封闭区间可以用连接其两圆心的线段表示,这样能得到若干线段,于是问题变成,若干线段中,是否能组成一个封闭区域,且起始圆心在其内。
按顺时针扫描每一条边,如果点在里面,点与所有线段两个端点角度和为2π,如果逆时针来,就是-2π。 如果在多边形外的话,和必然是0,于是加边i j 边权为从起始圆心到I和到j的角度和(正负各加一条),如果整个图有负环,就相当于找到一个-2π,说明出不去了,spfa即可。。

#include <cstdio>
#include <iostream>
#include <cstring>
#include <queue>
#include <cmath>
#include <algorithm>
using namespace std;
const int maxn = 340;
const double eps = 1e-8;
int n,tot,head[maxn],num[maxn];
bool inq[maxn];
struct Tcircle{
    double x,y,r;
}p[maxn];
double dis[maxn];
struct node{
    int u,v,next;
    double w;
}edges[maxn*maxn];
void add(int u,int v,double w){
    edges[tot].v = v;edges[tot].w = w;edges[tot].next = head[u];head[u] = tot++;
}
void init(){
    tot = 0;memset(head,-1,sizeof(head));
    scanf("%d",&n);
    for(int i = 1 ;i <= n ; i++){
        scanf("%lf%lf%lf",&p[i].x,&p[i].y,&p[i].r);
    }
    scanf("%lf%lf%lf",&p[0].x,&p[0].y,&p[0].r);
    for(int i = 1; i <= n ; i++)
        p[i].r += p[0].r;
}
double dist(int i,int j){
    return sqrt( (p[i].x-p[j].x)*(p[i].x-p[j].x)+(p[i].y-p[j].y)*(p[i].y-p[j].y) );
}
bool spfa(){
    queue<int>q;
    for(int i = 1;i <= n;i++){
        q.push(i);
        inq[i] = true;
        num[i] = 0;
        dis[i] = 0.0;
    }
    while(!q.empty()){
        int tmp = q.front();q.pop();
        inq[tmp] = 0;
        for(int k = head[tmp];k != -1;k = edges[k].next){
            if(dis[edges[k].v] > eps+dis[tmp]+edges[k].w){
                dis[edges[k].v] = dis[tmp]+edges[k].w;
                if(!inq[edges[k].v]){
                    inq[edges[k].v] = true;
                    q.push(edges[k].v);
                    num[edges[k].v]++;
                    if(num[edges[k].v] > n-1)       return true;
                }
            }
        }
    }
    return false;
}
void sov(){
    for(int i = 1; i <= n ; i++){
        for(int j = i+1 ; j <= n ; j++){
            if(p[i].r+p[j].r-dist(i,j) <= eps)   continue;
            double ang = ( (p[i].x-p[0].x)*(p[j].x-p[0].x) + (p[i].y-p[0].y)*(p[j].y-p[0].y) )/dist(0,i)/dist(0,j);
            ang = acos(ang);
            if( (p[i].x-p[0].x) *(p[j].y-p[0].y) - (p[j].x-p[0].x) * (p[i].y-p[0].y) >= 0){
                add(i,j,ang);add(j,i,-ang);
            }
            else{
                add(i,j,-ang);add(j,i,ang);
            }
        }
    }
}
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        init();
        sov();
        if(spfa())  printf("NO\n");
        else    printf("YES\n");
        if(T)   printf("\n");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值