圆的国度:Can you understand what you see?

题目描述:

    平面上有 n 个没有公共点 的圆。你要从点(x1,y1)走到(x2,y2)。问你最少要经过多少圆的边界。保证这两个点都不在圆的边界上。

输入格式:

    问题输入: 第一行一个整数 n, 1<=n<=50。 接下来三行每行 n 个整数,分别表示 n 个圆的圆心和半径,格式如下: x1,x2……xi……xn , y1,y2……yi……yn , r1,r2……ri……rn         -1000<=xi,yi<=1000,1<=ri<=1000 。最后一行四个整数 x1,y1,x2,y2, -1000<=x1,y1,x2,y2<=1000。

输出格式:

    问题输出: 一个整数,意义如上。

样例:

输入:                                                          输入:

3                                                          1

0 -6  6                                                  0

0  1  6                                                  0

2  2  2                                                  2

-5  1  5  1                                             -5  1  5  1   

输出:

1                                                          0

 

luogu链接:https://www.luogu.com.cn/problem/T139630


 

   相信大家看到这道题的第一印象都是:

     好 ~~一 ~~道 ~~水~~题 ~~      

     循环输入这些玩意,把起点终点一连,写个计数器,搞几个判断不就完了吗,这题不是有手就行???

   然而,事实真的如此吗?

   根据题意,我们很容易画出样例图:

 (样例一)(请原谅我拙劣的画图技术)

 WTF?!?!?!?!

这明明经过了2个圆的边界,为啥子样例输出是1???

难道样例有问题???

样例二也有相同的问题,在图上穿过了一个一个圆,而输出却是0。

经验告诉我们样例错误的情况极少出现,更何况一次错两个。出问题的一定是我们自己。

 


 

我们不妨重新审视一下题目。

注意【你要从点(x1,y1)走到(x2,y2)】,题目中仅仅说走到,却丝毫未提及怎么走。难道道路只有连接两点的一条吗?显然并非如此。题目中没有说必须走直线,翻译过来就是:只要能到,你怎么蛇皮走位都没问题。

于是,样例一便可以这么走:

                                                             

甚至这样走:

 

这样都满足了从一点到另一点的要求,而且只穿过一个圆,符合样例输出。

结合上图可知,与穿过几个圆直接挂钩的是起点和终点是否在圆内,若在,则会穿过一圆

而点是否在圆内,只需要比较点到圆心的距离与该圆半径,若距离>半径,则点在圆外,若距离<半径,则点在圆内(数据保证点不在圆上)。

故我们可以写出程序:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
int n,x[60],y[60],r[60];
int s1,s2,s3,x01,y01,x02,y02;
int main()
{
    scanf("%d",&n);
    int count=0;
    for(int i=1;i<=n;i++)  //注意输入圆心和半径要分开输入
    {
        scanf("%d",&x[i]);
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&y[i]);
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&r[i]);
    }
    scanf("%d%d%d%d",&x01,&y01,&x02,&y02);
    for(int i=1;i<=n;i++)
    {
        s1=sqrt((x01-x[i])*(x01-x[i])+(y01-y[i])*(y01-y[i]));  //计算点到圆心距离
        s2=sqrt((x02-x[i])*(x02-x[i])+(y02-y[i])*(y02-y[i]));
        if(s1<r[i])  count++;  //判断点到圆心距离和半径哪个大
        if(s2<r[i])  count++; 
    }
    printf("%d",count);
    return 0;
}

这段程序看上去没什么问题,两个样例也都能过,那就交吧!

然而…………

 

 WA~~~~~~~~~~~~~~~~~~~~

最后一个测试点没有过,说明还有问题。

遇到只错了一个测试点的情况,我们该怎么办呢?

当然是卡测试点继续debug。

我们上上方的程序中判断计数是否加一,是通过比较点到圆心距离和圆的半径,但是这种操作并不适用于一种较特殊的情况,如图:

 

 很明显,这种情况下从A到B需要穿过的圆的数目为0,但若以我们的评测标准,因为A和B到圆心距离都小于圆的半径,计数会加2,所以得出的答案是2,明显错误。这就是两点在同一圆内的特殊情况,需要另加判断。

我们很容易发现,在这种情况下,两个点到圆心的距离都小于半径,而之前的情况是一个点到圆心距离小于半径、一个大于半径,于是,我们便可以由此写出特判程序:

if(s1<r[i]&&s2<r[i])  continue;  //使用continue直接跳过此情况。注意,要先判断是否在同一圆内,否则计数会先加2
else if(s1<r[i]||s2<r[i])  count++;   

到此这道题就全部结束了,AC代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
int n,x[60],y[60],r[60];
int s1,s2,s3,x01,y01,x02,y02;
int main()
{
    scanf("%d",&n);
    int count=0;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&x[i]);
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&y[i]);
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&r[i]);
    }
    scanf("%d%d%d%d",&x01,&y01,&x02,&y02);
    for(int i=1;i<=n;i++)
    {
        s1=sqrt((x01-x[i])*(x01-x[i])+(y01-y[i])*(y01-y[i]));
        s2=sqrt((x02-x[i])*(x02-x[i])+(y02-y[i])*(y02-y[i]));
        if(s1<r[i]&&s2<r[i])  continue;
        else if(s1<r[i]||s2<r[i])  count++; 
    }
    printf("%d",count);
    return 0;
}

小蒟蒻的此篇题解就到此结束了,喜欢的话还请留下一个大拇指。

 

 

 

posted @ 2020-07-20 17:23  Na2S2O3  阅读(417)  评论(1编辑  收藏  举报