CF333E Summer Earnings 枚举+优化

题目链接http://codeforces.com/problemset/problem/333/E

题意

在一个平面内给出(n)个点的坐标,任选其中三个为圆心作半径相同的圆,要求这三个圆不能相交但可以相切,求能画出的圆中的最大半径。

分析

首先要知道任取三个点,符合题意的最大半径是多少。
先考虑三点不共线的情况,由数学知识可以知道这三点能构成一个三角形。

因为三个圆最多只能相切,所以最大的直径就是最小的边。
三点共线的时候是一种特殊情况,但也和这个一样,最大的半径是任意两点间的最小距离的一半。
所以这个问题就简化成了,找出三个点连成三条边,使得这三条边中的最小值最大。这个比较好解决,把边权从大到小排一边序,当第一次有三个点连通时输出答案就行。
下面就是怎么判断三点是否连通,直接枚举三个点的话肯定会T掉,这时我们就要用到暴力神器(bitset)(bitset)存储二进制的状态,用第(i)(bitset)的第(j)个二进制位表示(i)(j)是否连通,然后我们枚举边,设边的起点为(u)终点为(v),如果能找到第三点(k)使得(u)(k)连通,(v)(k)连通,那么加上这条边的时候,这三点就连通了,否则把相应的二进制位设置成1。看起来很繁琐但是(bitset)的两个函数(count)(set)能帮助我们很好的解决问题,前者的作用看名字就能看出来,(set)是将某位置设置成1,于是此题得解。

#include<cstdio>
#include<cmath>
#include<bitset>
#include<algorithm>
using namespace std;
const int N=3010;
struct Edge{
    int fro,to;double val;
    bool operator <(const Edge&A)const{
        return val>A.val;
    }
    Edge(){}
    Edge(int a,int b,double t){
        fro=a;to=b;val=t;
    }
}e[N*N];int len;
struct Node{
    int x,y;
}point[N];
double dis(int x,int y,int xx,int yy){
    return sqrt((x-xx)*(x-xx)+(y-yy)*(y-yy));
}
bitset<N> q[N];
int main(){
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d%d",&point[i].x,&point[i].y);
    for(int i=1;i<=n;i++)
        for(int j=1;j<i;j++)
            e[++len]=Edge(i,j,dis(point[i].x,point[i].y,point[j].x,point[j].y));
    sort(e+1,e+len+1);
    for(int i=1;i<=len;i++){
        int u=e[i].fro,v=e[i].to;
        if((q[u]&q[v]).count()){
            printf("%.20lf
",e[i].val/2.0);
            return 0;
        }
        q[u].set(v);
        q[v].set(u);
    }
}
原文地址:https://www.cnblogs.com/anyixing-fly/p/12704439.html