POJ--3164--Command Network【朱劉算法】最小樹形圖
鏈接:http://poj.org/problem?id=3164
題意:告訴n個點坐標,m條邊表示兩個點之間有路。從1點開始建立一個有向圖最小生成樹。
朱劉算法模板題
========================== 切割線之下摘自
user_id=Sasuke_SCUT" style="color:rgb(202,0,0); text-decoration:none; font-family:Arial; font-size:14px; line-height:26px">Sasuke_SCUT的blog==================================================
最 小樹形圖,就是給有向帶權圖中指定一個特殊的點root,求一棵以root為根的有向生成樹T。而且T中全部邊的總權值最小。最小樹形圖的第一個算法是 1965年朱永津和劉振宏提出的復雜度為O(VE)的算法。
推斷是否存在樹形圖的方法非常easy,僅僅須要以v為根作一次圖的遍歷就能夠了,所以以下的 算法中不再考慮樹形圖不存在的情況。
在全部操作開始之前,我們須要把圖中全部的自環全都清除。非常明顯,自環是不可能在不論什麽一個樹形圖上的。僅僅有進 行了這步操作,總算法復雜度才真正能保證是O(VE)。
首先為除根之外的每一個點選定一條入邊,這條入邊一定要是全部入邊中最小的。
如今全部的最小 入邊都選擇出來了。假設這個入邊集不存在有向環的話,我們能夠證明這個集合就是該圖的最小樹形圖。這個證明並非非常難。假設存在有向環的話,我們就要將這 個有向環所稱一個人工頂點。同一時候改變圖中邊的權。假設某點u在該環上。並設這個環中指向u的邊權是in[u]。那麽對於每條從u出發的邊(u, i, w),在新圖中連接(new, i, w)的邊,當中new為新加的人工頂點; 對於每條進入u的邊(i, u, w),在新圖中建立邊(i, new, w-in[u])的邊。為什麽入邊的權要減去in[u],這個後面會解釋。在這裏先給出算法的步驟。然後能夠證明,新圖中最小樹形圖的權加上舊圖中被收縮 的那個環的權和,就是原圖中最小樹形圖的權。
上面結論也不做證明了。如今根據上面的結論,說明一下為什麽出邊的權不變,入邊的權要減去in [u]。對於新圖中的最小樹形圖T,設指向人工節點的邊為e。
將人工節點展開以後,e指向了一個環。
如果原先e是指向u的,這個時候我們將環上指向u的邊 in[u]刪除,這樣就得到了原圖中的一個樹形圖。我們會發現,如果新圖中e的權w‘(e)是原圖中e的權w(e)減去in[u]權的話,那麽在我們刪除 掉in[u],而且將e恢復為原圖狀態的時候,這個樹形圖的權仍然是新圖樹形圖的權加環的權,而這個權值正是最小樹形圖的權值。所以在展開節點之後,我們 得到的仍然是最小樹形圖。
逐步展開全部的人工節點,就會得到初始圖的最小樹形圖了。
假設實現得非常聰明的話。能夠達到找最小入邊O(E),找環 O(V),收縮O(E)。當中在找環O(V)這裏須要一點技巧。這樣每次收縮的復雜度是O(E),然後最多會收縮幾次呢?因為我們一開始已經拿掉了全部的 自環,我門能夠知道每一個環至少包括2個點。收縮成1個點之後。總點數降低了至少1。
當整個圖收縮到僅僅有1個點的時候,最小樹形圖就不不用求了。所以我們最 多僅僅會進行V-1次的收縮。所以總得復雜度自然是O(VE)了。由此可見。假設一開始不除去自環的話。理論復雜度會和自環的數目有關。
======================== 切割線之上摘自
user_id=Sasuke_SCUT" style="color:rgb(202,0,0); text-decoration:none; font-family:Arial; font-size:14px; line-height:26px">Sasuke_SCUT的blog=====================================================
簡單的說就是除源點外每一個點選一條權值最小的入邊,假設存在環則說明還存在多余的邊,把成環的點縮成一個點再進行一遍生成樹,直到沒有環。
朱劉算法模板,頂點下標從0開始
/* 最小樹形圖圖模版-朱劉算法 模版說明:點標號必須0-(N-1) 必須去除到自身的點(到自身的邊的邊權賦無限大) */ #include<cstring> #include<string> #include<fstream> #include<iostream> #include<iomanip> #include<cstdio> #include<cctype> #include<algorithm> #include<queue> #include<map> #include<set> #include<vector> #include<stack> #include<ctime> #include<cstdlib> #include<functional> #include<cmath> using namespace std; #define PI acos(-1.0) #define MAXN 50100 #define eps 1e-7 #define INF 0x7FFFFFFF #define seed 131 #define mod 1000000007 #define ll long long #define ull unsigned ll #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 struct node{ int u,v; double dis; }edge[10100]; int pre[110],ID[110],vis[110]; int n,m; int x[110],y[110]; double In[110]; double Directed_MST(int root,int NV,int NE) { double ret = 0; while(true) { //1.找最小入邊 for(int i=0;i<NV;i++) In[i] = INF; for(int i=0;i<NE;i++){ int u = edge[i].u; int v = edge[i].v; if(edge[i].dis < In[v] && u != v) { pre[v] = u; In[v] = edge[i].dis; } } for(int i=0;i<NV;i++) { if(i == root) continue; if(In[i] == INF) return -1;//除了根以外有點沒有入邊,則根無法到達它 } //2.找環 int cntnode = 0; memset(ID,-1,sizeof(ID)); memset(vis,-1,sizeof(vis)); In[root] = 0; for(int i=0;i<NV;i++) {//標記每一個環 ret += In[i]; int v = i; while(vis[v] != i && ID[v] == -1 && v != root) { vis[v] = i; v = pre[v]; } if(v != root && ID[v] == -1) { for(int u = pre[v] ; u != v ; u = pre[u]) { ID[u] = cntnode; } ID[v] = cntnode ++; } } if(cntnode == 0) break;//無環 for(int i=0;i<NV;i++) if(ID[i] == -1) { ID[i] = cntnode ++; } //3.縮點,又一次標記 for(int i=0;i<NE;i++) { int v = edge[i].v; edge[i].u = ID[edge[i].u]; edge[i].v = ID[edge[i].v]; if(edge[i].u != edge[i].v) { edge[i].dis -= In[v]; } } NV = cntnode; root = ID[root]; } return ret; } int main(){ int i,j,a,b; double temp; while(scanf("%d%d",&n,&m)!=EOF){ for(i=1;i<=n;i++){ scanf("%d%d",&x[i],&y[i]); } for(i=0;i<m;i++){ scanf("%d%d",&a,&b); edge[i].u = a - 1; edge[i].v = b - 1; if(a==b) edge[i].dis = INF; else{ temp = (x[a]-x[b])*(x[a]-x[b])+(y[a]-y[b])*(y[a]-y[b]); temp = sqrt(temp); edge[i].dis = temp; } } double ans = Directed_MST(0,n,m); if(ans==-1) puts("poor snoopy"); else printf("%.2lf\n",ans); } return 0; }
POJ--3164--Command Network【朱劉算法】最小樹形圖