1. 程式人生 > >2018.11.02【校內模擬】飛越行星帶(最小生成樹)

2018.11.02【校內模擬】飛越行星帶(最小生成樹)

傳送門

解析:

最小生成樹的優秀做法。

建圖很妙啊,把所有點對之間建立距離為權值的邊,然後所有點向頂部連權值為距離的邊,向底部連權值為LyL-y的邊,然後求一個最小生成樹,將頂部和底部連在一起的邊的權值就是答案。

我解釋一下為什麼這樣做是對的。 考慮我們現在有一艘直徑為當前列舉邊的飛碟,我們禁止它通過這條邊,如果它還可以飛到另一端,說明它穿過了一些權值大於它的直徑的邊,那麼它的直徑可以繼續增大。而上下連在一起,就形成了一個圍欄,它就永遠不可能到達另一端。此時它的直徑就已經達到了最大。

程式碼:

#include<bits/stdc++.h>
using namespace
std; #define ll long long #define re register #define gc getchar #define pc putchar #define cs const inline int getint(){ re int num; re char c; re bool f=0; while(!isdigit(c=gc()))if(c=='-')f=1;num=c^48; while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48); return f?-num:num; } cs int
N=502; struct edge{ int u,v; double w; friend bool operator<(cs edge &a,cs edge &b){ return a.w<b.w; } }E[N*N];int ecnt; inline void addedge(int u,int v,double w){ E[++ecnt]=(edge){u,v,w}; } int fa[N]; inline int getfa(int u){ while(u^fa[u])u=fa[u]=fa[fa[u]]; return u; } int
n,L; int x[N],y[N]; inline double dist(int i,int j){ return sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j])); } signed main(){ n=getint();L=getint(); for(int re i=1;i<=n;++i){ fa[i]=i; x[i]=getint();y[i]=getint(); for(int re j=1;j<i;++j)addedge(i,j,dist(i,j)); addedge(0,i,y[i]); addedge(n+1,i,L-y[i]); } fa[n+1]=n+1; sort(E+1,E+ecnt+1); for(int re i=1;i<=ecnt;++i){ int u=getfa(E[i].u); int v=getfa(E[i].v); if(u==v)continue; fa[v]=u; if(getfa(0)==getfa(n+1)){ printf("%.3f",E[i].w); return 0; } } return 0; }