【前言】話說好久沒有寫題解了。到暑假了反而忙。o(╯□╰)o
【原題】
2458: [BeiJing2011]最小三角形
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 574 Solved: 177
[Submit][Status]
Description
Xaviera現在遇到了一個有趣的問題。
平面上有N個點,Xaviera想找出周長最小的三角形。
由於點非常多,分佈也非常亂,所以Xaviera想請你來解決這個問題。
爲了減小問題的難度,這裏的三角形也包括共線的三點。
Input
第一行包含一個整數N表示點的個數。
接下來N行每行有兩個整數,表示這個點的座標。
Output
輸出只有一行,包含一個6位小數,爲周長最短的三角形的周長(四捨五入)。
Sample Input
1 1
2 3
3 3
3 4
Sample Output
HINT
100%的數據中N≤200000。
Source
【分析】今天新學瞭解決這類問題的方法——分治。沒錯,就是分治。
先講一下n是10^5級別的平面最近點對吧(CF 245 DIV 2 D)。很容易懂。
詳細的原理可以參考這個博客,講的很詳細。(很多時候只要感性認識原理即可)下面講一下具體做法。
①對於平面上的點,按x座標排序(這是永久排序)。
②每次遞歸(l,r),函數的返回值是第l個到第r個之間的所有點的最近點對。
③如果l=r,那麼返回無窮大;如果l+1=r,就直接返回兩個點的距離。
④每次先遞歸(l,mid)和(mid+1,r)。顯然,這兩個會有兩個返回值,不妨設爲d1和d2。首先我們設D=MIN(d1,d2),即當前的最優值暫時是D。
⑤顯然,還有一種情況。左邊那塊的某個點和右邊那塊的某個點產生關係。那麼,我們可以從mid這個位置向左跑到mid-D,向右跑到mid+D,然後把這一段中的點都拎出來——因爲只有這兩段中的點纔有可能產生小於D的貢獻。
⑥這時候我們要意識到潛在複雜度的保證(實際原理也不難懂呵)。首先,如果直接枚舉兩兩點要N^2。我們先把拎出來的點按y排序。(NlogN)然後看似也是N^2的枚舉,只是加了一個優化(從底下開始枚舉i,如果Y[J]-Y[I]>D就直接break)——這樣可證明幾乎是線性。總複雜度N*logN*logN。
再講一下本題,也是差不多道理。因爲是三角形,我們把一些細節改一下即可。
【代碼】
#include<cstdio>
#include<cmath>
#include<algorithm>
#define N 200005
#define INF 210000000000.0
using namespace std;
struct arr{int x,y;}a[N],num[N];int n,i,Test;
inline bool cmpx(const arr &a,const arr &b){return a.x<b.x;}
inline bool cmpy(const arr &a,const arr &b){return a.y<b.y;}
inline double dis(const arr &a,const arr &b){return sqrt((a.x-b.x)*1.*(a.x-b.x)+(a.y-b.y)*1.*(a.y-b.y));}
inline double work(int l,int r)
{
if (l==r) return INF;
if (l+1==r) return INF;
if (l+2==r) return dis(a[l],a[l+1])+dis(a[l+1],a[r])+dis(a[l],a[r]);
int mid=(l+r)>>1;
double d1=work(l,mid),d2=work(mid+1,r);
double D=min(d1,d2),ans=D,DD=D/2.0;int cnt=0;
for (int i=l;i<=r;i++)
if (fabs(a[mid].x-a[i].x)<=DD) num[++cnt]=a[i];
sort(num+1,num+cnt+1,cmpy);
for (int i=1;i<cnt-1;i++)
for (int j=i+1;j<cnt;j++)
{
if (num[j].y-num[i].y>DD) break;
for (int k=j+1;k<=cnt;k++)
{
if (num[k].y-num[i].y>DD) break;
double temp=dis(num[i],num[j])+dis(num[i],num[k])+dis(num[j],num[k]);
if (temp<ans) ans=temp;
}
}
return ans;
}
int main()
{
read(n);//讀入優化就不貼了。
for (i=1;i<=n;i++)
read(a[i].x),read(a[i].y);
sort(a+1,a+n+1,cmpx);
printf("%.6lf",work(1,n));
return 0;
}