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\n",e[i].val/2.0);
return 0;
}
q[u].set(v);
q[v].set(u);
}
}
int - > long long
0 - > 100