[csp模拟4] T2 —— ZJM要抵御宇宙射线

题意

在这里插入图片描述

Input

在这里插入图片描述

Output

输出包括两行
在这里插入图片描述

输入样例

在这里插入图片描述

输出样例

在这里插入图片描述

提示

在这里插入图片描述


分析


  • 题目分析

这道题说实话,最开始我也有点没明白意思,就是边做边理解。不过三七二十一,写出来再说。

这道题想让我们找到的其实就是所有给出点中的一个,使得以这个点为圆心建立的圆能覆盖所有其他点。

最开始思考确实容易想复杂,比如最开始我就在想这或许是一道图论。

这道题的本质其实就是找到所有点中,和其他点最大距离最小的一个点。所以我最开始觉得或许和求图中最远最小距离有关。但其实这道题比较单纯。

假设取任意一点为圆心,要想覆盖所有点,则半径显然至少等于离该点最远的点到该点的距离。要使圆半径最小,那自然就是等于到最远点的距离。

因此只要依次求出每个点到其他所有点之间的距离,再找出其中最小的最大距离就行。这里求最小最大值也不需要用到二分。

对每个点做为圆心,遍历其他所有点。若当前遍历点序号小于当前圆心点,则说明两点之间的距离已知,直接调用比较;否则进行计算。若该圆心存在与其他任意一点的距离大于之前所标记的最小最大距离,则该圆心点一定不合法。若遍历完全后,该点符合要求,则进行更新;若该点的最大距离等于当前记录的最小最大距离,则按照x和y更小优先级原则判断是否需要更新。

这道题还有一个故意吓人的地方,保留两位小数其实是唬人的。所有坐标都是整数,因此所有坐标距离的平方也都是整数,因此不必受此影响。


  • 问题

需要注意的是,在遍历过程中发现当前圆心点不合法时,并不能直接结束当前遍历。因为每一次的遍历除了是判定当前圆心点是否合法,实际上也是计算所有点之间距离的过程。如果提前结束对不合法圆心点的遍历,就代表着与该圆心点相连的某些点之间的距离未知(实际上是保持初始化为0),这样就会影响之后对其他圆心点的判定,导致在某些情况下结果错误。


总结

  1. 读不懂题还是不大行,但是还好这道题我没想太多😿

代码

//
//  main.cpp
//  lab2
//
//

#include <limits.h>
#include <float.h>
#include <cmath>
#include <iomanip>
#include <map>
#include <iostream>
//#define DBL_MAX 1.7976931348623158e+308
using namespace std;

pair<double, double> bot[1005];     //记录所有坐标

double counting(pair<double, double> a,pair<double, double> b)      //计算距离的2次方
{
    return pow(a.first - b.first,2) + pow(a.second - b.second,2);
}

map<pair<int,int>,double> distances;        //将两点与两点距离进行映射

int main()
{
    ios::sync_with_stdio(false);
    
    int n = 0,center = 0,p = -1;
    double max_dis = -1,min_r = DBL_MAX;
    
    cin>>n;
    
    for( int i = 0 ; i < n ; i++ )              //输入所有点
        cin>>bot[i].first>>bot[i].second;
    
    
    for( int i = 0 ; i < n ; i++ )          //以当前遍历的i点为圆心
    {
        
        for( int j = 0 ; j < n ; j++ )      //对当前点遍历其他所有点
        {
            if( j == i )            //跳过自己
                continue;
            
            if( j < i + 1 )      //若当前遍历点编号小于i,说明两点之间的距离已经被计算出,不需要重新计算
            {
                
                //若当前距离大于以i点为圆心的最大半径,则更新
                if( distances[make_pair(j ,i)] > max_dis )
                    max_dis = distances[make_pair(j ,i)];
                
                //若存在一个距离已经大于当前最小半径,该点不可能为圆心
                //不能直接结束,因为对于i点可能还存在一些点,两点之间的距离未计算
                //如果提前结束,会导致其后的点在作为圆心遍历点时,有些距离未知
                if( distances[make_pair(j, i)] > min_r )
                    max_dis = -1;
                
                
            }
            else if( j >= i + 1 )       //若当前遍历点在i之后,则这些距离需要计算
            {
                
                //计算i到j点的距离2次方
                distances[make_pair(i, j)] = counting(bot[i], bot[j]);
                    
                 //若当前距离大于以i点为圆心的最大半径,则更新
                if( distances[make_pair(i, j)] > max_dis )
                    max_dis = distances[make_pair(i, j)];
                
                //若存在一个距离已经大于当前最小半径,该点不可能为圆心
                if( distances[make_pair(i, j)] > min_r )
                    max_dis = -1;
 
            }
        
        }
        
        //若当前点可能为圆心,则更新圆心点
        //并用该点与其他点之间的最大距离更新最小圆半径
        if( max_dis != -1 && min_r > max_dis)
        {
            min_r = max_dis;
            center = i;
        }
        else if( min_r == max_dis && max_dis != -1 )        //保证多解时,优先级为小
        {
            if( bot[i].first < bot[center].first )
                center = i;
            else if ( bot[i].first == bot[center].first )
            {
                if( bot[i].second < bot[center].second )
                    center = i;
            }
        }
        
        max_dis = -1;
    }

    
    //输出答案
    cout<<fixed<<setprecision(2)<<bot[center].first<<" ";
    cout<<fixed<<setprecision(2)<<bot[center].second<<endl;
    
    cout<<fixed<<setprecision(2)<<min_r<<endl;;
    
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天翊藉君

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值