https://www.luogu.com.cn/problem/P1429
思路一:正經解法:分治。首先把個點按照排序,每次按照把點集分成兩部分,得到每一部分點對之間的最小值。那麼總體最小值要麼等於,要麼等於左右兩部分各選一個點組成的點對之間的距離。現在考慮後半部分怎麼算,暴力枚舉肯定是不行的,我們可以利用已知信息——來入手,顯然可以把需要考慮的點的的範圍限定到,此時畫圖可以知道對於任意一個位於該範圍內的點,滿足的點的數量不超過個,那麼只要我們把這個範圍內的點按照排序後,就可以在內通過枚舉得到結果。但是這樣複雜度是的,還可以再優化嗎?可以,按照排序的過程沒必要利用來實現,我們這個過程本來就是遞歸求解的,所以可以直接利用歸併排序的過程實現排序,這樣複雜度就降到了,同時爲了減少運算,代碼中使用的是距離的平方進行比較,看代碼的時候要注意一下~。
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
using ll=long long;
const int maxn=2e5+5;
struct point
{
ll x,y;
point(){}
point(ll x,ll y):x(x),y(y){}
bool operator <(const point &a)const
{
if(x==a.x)
return y<a.y;
return x<a.x;
}
}p[maxn],tmp[maxn];
int n;
int idx[maxn];
inline ll dis(point a,point b)
{
return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);
}
inline void Merge(int l,int mid,int r)//歸併排序
{
int i=l,j=mid+1,k=l;
while(i<=mid&&j<=r)
{
if(p[i].y<=p[j].y)//按照y
tmp[k++]=p[i++];
else
tmp[k++]=p[j++];
}
while(i<=mid)
tmp[k++]=p[i++];
while(j<=r)
tmp[k++]=p[j++];
for(int i=l;i<=r;i++)
p[i]=tmp[i];
}
ll solve(int l,int r)
{
if(l==r)//一個點
return 2e18; //返回一個無意義的極限值
if(l+1==r)//兩個點
{
if(p[l].y>p[r].y)//別忘了這一步
swap(p[l],p[r]);
return dis(p[l],p[r]);
}
int mid=(l+r)>>1;
ll base=p[mid].x; //記錄基準值
ll ans=solve(l,mid);
ans=min(ans,solve(mid+1,r));
Merge(l,mid,r); //按y進行歸併排序
int cnt=0;
ll v;
for(int i=l;i<=r;i++)
{
v=abs(base-p[i].x);
v*=v;
if(v<ans)
idx[++cnt]=i; //計算出位於所限制區間內的點
}
for(int i=1;i<=cnt;i++)
{
for(int j=i+1;j<=cnt;j++)
{
v=abs(p[idx[j]].y-p[idx[i]].y);
v*=v;
if(v>=ans)
break; //只計算縱座標之差的絕對值小於預定值 的點對
ans=min(ans,dis(p[idx[i]],p[idx[j]]));
}
}
return ans;
}
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%lld %lld",&p[i].x,&p[i].y);
sort(p,p+n);
printf("%.4f\n",sqrt(solve(0,n-1)));
return 0;
}
思路二:暴力+二分剪枝。首先還是要對個點按照排序,然後枚舉每一個點,因爲已知距離最小值爲,所以在枚舉第個點的時候,可以把限制在之內,因爲點集按照是有序的,所以我們可以通過二分找到這個限制所對應的區間,然後計算和之間的點對的距離。這個複雜度就比較玄學了,不過還是可以掉這道題的,而且代碼非常好寫。
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
using ll=long long;
const int maxn=2e5+5;
struct point
{
ll x,y;
point(){}
point(ll x,ll y):x(x),y(y){}
bool operator <(const point &a)const
{
if(x==a.x)
return y<a.y;
return x<a.x;
}
}p[maxn];
int n;
inline double dis(point a,point b)
{
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%lld %lld",&p[i].x,&p[i].y);
sort(p,p+n);
double ans=2e9;
int l,r;
point base(0,0);
for(int i=0;i<n;i++)
{
base.x=p[i].x-ans-1;
l=lower_bound(p,p+n,base)-p;
base.x=p[i].x+ans+1;
r=upper_bound(p,p+n,base)-p-1;
while(l<=r)
{
if(i==l)
++l;
else
ans=min(ans,dis(p[i],p[l++]));
}
}
printf("%.4f\n",ans);
return 0;
}