1. 程式人生 > >POJ--3164--Command Network【朱劉算法】最小樹形圖

POJ--3164--Command Network【朱劉算法】最小樹形圖

-- com 刪除 spa col namespace sca while 我們

鏈接: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【朱劉算法】最小樹形圖