3/29 百度笔试第三题ac代码
题目
给定M*N的区域,给定一些点的坐标代表星球,我们需要尽量避开上下边界和星球,如果我要从左边界到右边界,任意点出发和停止,请问离所有星球和上下边界的距离的最小值最大是多少?
思路
核心思想就是如果说存在一个半径r,使得每个星球按这个半径r画一个圆时,将会覆盖从上到下的一整条路径(路径堵住了)就认为走不通
- 按y值给点排序,也就是说按星球的纵向坐标值给星球排序
- 因为答案是浮点数,一般采用二分法求,我们二分半径r,最小为0,最大为M/2(M/2时将充满屏幕)
- 两个星球的距离如果小于2r,则认为这两个星球合在一起,合在一起的用并查集来维护,这里注意一点,如果暴力的话时O(N2)就超时了,因为我们之前排过序,所以对于一个星球我们就可以只找y到y+2r之间的星球(小于y的之前已经算过了,剩下的一定大于2*r)
- 如果星球的y值小于2r则认为星球和下边界合并了,如果M-y<2r则认为上边界和星球合并了
- 检查上和下是否合在一起了,如果上下合并了则可以认为上下已经打通,左右已经走不通也就是说无法从左到右了
- 一直二分直到找到r走得通和走不通的边界点
#include <map>
#include <vector>
#include <queue>
#include <iostream>
#include <algorithm>
#include <map>
#include <cmath>
using namespace std;
using ll=long long;
int find(vector<int> &arr,int index){
if(arr[index]<0)
return index;
return arr[index]=find(arr,arr[index]);
}
void unino(vector<int> &arr,int index1,int index2){
if(find(arr,index1)!=find(arr,index2)){
arr[find(arr,index1)]=find(arr,index2);
}
}
int main(){
int N,M,K;
cin>>N>>M>>K;
vector<pair<double,double>> plant(K);
for(int i=0;i<K;i++){
int r,c;
cin>>r>>c;
plant[i].first=c;
plant[i].second=r;
}
sort(plant.begin(),plant.end());
for(int i=0;i<K;i++){
swap(plant[i].first,plant[i].second);
}
double start=0,end=M*1.0/2;
while(end-start>1e-6){
double mid=(end+start)/2;
vector<int> disjoin_set(K+2,-1);
for(int i=0;i<K;i++){
for(int j=i+1;j<K;j++){
double disx=plant[j].first*1.0-plant[i].first*1.0;
if(disx>2*mid)
continue;
double disy=plant[j].second*1.0-plant[i].second*1.0;
if(disy>2*mid)
break;
if(sqrt(disx*disx+disy*disy)<2*mid){
unino(disjoin_set,i,j);
}
}
if(plant[i].second*1.0<2*mid){
unino(disjoin_set,i,K);
}
if(M*1.0-plant[i].second*1.0<2*mid){
unino(disjoin_set,i,K+1);
}
}
if(find(disjoin_set,K)==find(disjoin_set,K+1)){
end=mid;
}
else{
start=mid;
}
}
printf("%.4lf",(start+end)/2);
}
有评论说第一题,其实很简单,最小公倍数(A,B)=AB/最大公约数(A,B)
那么最小公倍数-最大公约数=AB/(最大公约数)-最大公约数,
这个函数关于AB递增,关于最大公约数递减,也就是说我们要AB最大,最大公约数最小
A和A-1一定互素,公约数为1,A*(A-1)的大小也仅次于AA,所以答案就是A(A-1),注意要用longlong
第二题
https://blog.csdn.net/yhf_naive/article/details/105186292