1. 程式人生 > >最小樹形圖(朱劉演算法模板)

最小樹形圖(朱劉演算法模板)

求有固定根的最小樹形圖的演算法

演算法步驟:

(1)求最短弧集:除了根節點外,找到所有其他的節點最小邊權的入邊(用in陣列記錄到達改點的最小邊權,用pre陣列記錄其父節點)

(2)檢驗生成的集合中是否存在有向圈,有的話進行步驟3,沒有進行步驟4,假如除了根節點外有的節點是孤立的,也就是沒有弧指向他,不存在最小樹形圖;

(判斷方法:利用pre陣列以每個點進行列舉搜尋)

(3)把有向環縮成一個點,形成(U1,U2,U3,U4.....Un')共n‘個點定義:不在一個環中的兩個點分別為id[u],id[v],邊權是w[u][v]-=in[v];然後進行步驟1;

(4)ans就是答案:

程式:

int mini_tree(int root,int n,int m)//分別是樹根,節點數,邊數,序號從1開始
{
    int ans=0;
    int i,u;
    while(1)
    {
        for(i=1;i<=n;i++)
            in[i]=inf;
        for(i=1;i<=m;i++)
        {
            int u=edge[i].u;
            int v=edge[i].v;
            if(edge[i].w<in[v]&&u!=v)
            {
                in[v]=edge[i].w;
                pre[v]=u;
            }
        }//找最小的入邊
        for(i=1;i<=n;i++)
        {
            if(i==root)continue;
            ans+=in[i];//把邊權加起來
            if(in[i]==inf)//如果存在沒有入弧的點則不存在最小樹形圖
                return -1;
        }
        memset(id,-1,sizeof(id));
        memset(use,-1,sizeof(use));
        int cnt=0;
        for(i=1;i<=n;i++)//列舉每個點,搜尋找環
        {
            int v=i;
            while(v!=root&&use[v]!=i&&id[v]==-1)
            {
                use[v]=i;
                v=pre[v];
            }
            if(v!=root&&id[v]==-1)//當找到環的時候縮點編號
            {
                ++cnt;
                id[v]=cnt;
                for(u=pre[v];u!=v;u=pre[u])
                    id[u]=cnt;
            }
        }
        if(cnt==0)//如果沒有環結束程式
            break;
        for(i=1;i<=n;i++)//把餘下的不在環裡的點編號
            if(id[i]==-1)
                id[i]=++cnt;
        for(i=1;i<=m;i++)//建立新的圖
        {
            int u=edge[i].u;
            int v=edge[i].v;
            edge[i].u=id[u];
            edge[i].v=id[v];
            if(edge[i].u!=edge[i].v)
                edge[i].w-=in[v];
        }
        n=cnt;//更新節點數和根節點的編號
        root=id[root];
    }
    return ans;
}