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