Uva 11853 Paintball

题目大意

给定一张1000*1000的图,要求从左边走到右边,无论起点还是终点都要尽可能的向上。
再给定N组Ball,给出Ball的坐标(x,y)和其半径r,要求在从左往右走的过程不能进入Ball的范围中。
答案要求输出起点和终点坐标,如果无法从左走向右那么输出IMPOSSIBL。

思路

看起来是个比较平凡的BFS求最短路问题,但仔细分析后发现如果直接BFS+边走边判的时间复杂的是
10^9,是必然超时的。所以不能直接套BFS。既然正着思考(类似模拟–言听计从)行不通,不妨试试逆向思考。从
障碍物Ball出发,如果存在一条“踩着“Ball自上边界到下边界的路,相当于此张图被Ball分割成了两半,那
么此时就肯定不能从左往右走了,可以直接输出IMPOSSIBLE,否则就必然存在一条从左往右的路。说回那条Ball路,在这
条路上,每经过一个Ball就判断是否与左边界x=0有交点,如果有交点,则更新起点坐标的值,对于右边
界x=1000同样如此。要注意起终点纵坐标设为1000。关于如果判断Ball和边界是否有交点的实现细节需要用到一点平面几何知识:
直线y=ax+bx+c,圆(x-a)^2 + (y-b)^2 =r^2
点(a,b)到直线y的距离|d|=|ax+by+c|/sqrt(a^2 + b^2 ),如果d^2 <= r^2,则Ball与边界有交点。在更新坐标值时要求交点坐标:
将直线与圆方程联立(直线即左右边界x1=0或x1=1000),可得
y=sqrt(r^2 -(x1-a)^2)+b
虽然y值有两个,但需除去在区间[0,1000]范围之外的,然后再取较大的哪一个。
由于此BFS是在Ball上跑的而1<=Ball<=1000,并且每次还需判断边界和寻找下一个Ball,则时间复杂度是O(Ball^2 )为10^6。

反思

对于需要BFS操作时,首先要先push进一个出发点,然后再while(!q.empty())进行下面的操作,而再push出发点时不要忘记更新坐标值。类比初始化vis[i]=1。此外,此题的思路很有意思,启示我们不要思维定势。对于那条Ball路,多画几个图就能理解其正确性了。

代码

#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cmath>
using namespace std;
struct Ball{
    int x,y,z;
};
Ball Circle[1005];
int n;
queue<Ball> q;

bool vis[1005];

bool dis(int x1,int y1,int r1,int x2,int y2,int r2)
{
    if((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)<=(r1+r2)*(r1+r2))return true;  //两点距离小于半径之和则两圆相交,等于则相切
    return false;
}
bool Line_Circle(int a,int b,int c,int x,int y,int r)  //边界线与圆相交
{
    if(r*r-(a*x+b*y-c)*(a*x+b*y-c)>=0)return true;  //点到线距离公式
    return false;
}
bool BFS()
{
    double left_y=1000,right_y=1000;
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=n;i++){  //找与上边界相交或相切的圆
        if(Line_Circle(0,1,1000,Circle[i].x,Circle[i].y,Circle[i].z)){
            double r=(double)Circle[i].z,a=(double)Circle[i].x,b=(double)Circle[i].y;
            if(Line_Circle(1,0,0,Circle[i].x,Circle[i].y,Circle[i].z)){
                double k=sqrt(r*r-a*a);
                if(k+b>=0&&k+b<=1000)left_y=min(left_y,k+b);
                if(-k+b>=0&&-k+b<=1000)left_y=min(left_y,-k+b);
            }
            if(Line_Circle(1,0,1000,Circle[i].x,Circle[i].y,Circle[i].z)){
                double k=sqrt(r*r-(1000-a)*(1000-a));
                if(k+b>=0&&k+b<=1000)right_y=min(right_y,k+b);
                if(-k+b>=0&&-k+b<=1000)right_y=min(right_y,-k+b);
            }
            q.push(Circle[i]);
            vis[i]=1;
        }
    }
    while(!q.empty()){
        Ball a;
        a=q.front();
        q.pop();
        if(Line_Circle(0,1,0,a.x,a.y,a.z)){  //圆与下边界有交点
            return false;
        }
        for(int i=1;i<=n;i++){
            if(!vis[i]&&dis(a.x,a.y,a.z,Circle[i].x,Circle[i].y,Circle[i].z)){
                double r=(double)Circle[i].z,a=(double)Circle[i].x,b=(double)Circle[i].y;
                if(Line_Circle(1,0,0,Circle[i].x,Circle[i].y,Circle[i].z)){
                    double k=sqrt(r*r-a*a);
                    if(k+b>=0&&k+b<=1000)left_y=min(left_y,k+b);
                    if(-k+b>=0&&-k+b<=1000)left_y=min(left_y,-k+b);
                }

                if(Line_Circle(1,0,1000,Circle[i].x,Circle[i].y,Circle[i].z)){
                    double k=sqrt(r*r-(1000-a)*(1000-a));
                    //printf("k=:%lf\n",k);

                    if(k+b>=0&&k+b<=1000)right_y=min(right_y,k+b);
                    if(-k+b>=0&&-k+b<=1000)right_y=min(right_y,-k+b);
                }
                q.push(Circle[i]);
                vis[i]=1;
            }
        }
    }
    printf("0.00 %.2lf 1000.00 %.2lf\n",left_y,right_y);
    return true;
}

int main()
{
    //freopen("1.txt","r",stdin);
    //freopen("2.txt","w",stdout);

    while(scanf("%d",&n)!=EOF){
        for(int i=1;i<=n;i++){
            scanf("%d%d%d",&Circle[i].x,&Circle[i].y,&Circle[i].z);
        }
        if(BFS()) continue;
        else printf("IMPOSSIBLE\n");
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值