次小生成树

来源:互联网 发布:淘宝热卖排行榜 编辑:程序博客网 时间:2024/05/21 09:39

O(V^2)

结论

次小生成树可由最小生成树转换一条边得到

证明

T是某一棵最小生成树,T0是任一棵异于T的树,通过变换T0->T1->T2->…->Tn(T)变成最小生成树,所谓的变换是,每次把T_i中的某条边换成T中的一条边,而且树T_(i + 1)的权小于等于T_i的权。 
具体操作是: 
step1. 在T_i中任取一条不在T中的边u_V; 
step2. 把边u_v去掉,就剩下两个连通分量A和B,在T中,必有唯一的边u’_v’连结A和B; 
step3. 显然u’v’的权比u_v小(否则,u_v就应该在T中),把u’_v’替换u_v即得到树T(i + 1); 
特别地:取Tn为任一棵次小生成树,T_(n - 1)也就是次小生成树且跟T差一条边,结论得证。

算法

只要充分利用上述结论,既得v^2的算法。具体如下: 
step1. 先用Prim求出最小生成树T,在Prim的同时,用一个矩阵MAX[u][v]记录在T中连结任意两点u,v的唯一的路中权值最大的那条边的权值。(注意这里),这是很容易做到的,因为Prim是每次增加一个结点s,而已经标好了的结点集合为w,则w中所有的结点到s的路中最大权值的边就是当前加入的这条边,用时O(V^2); 
step2.枚举所有不在T中的边u_v,加入边u_v替换权为MAX[u][v]的边,不断更新最小值,即次小生成树,用时O(E),故总用时O(V^2)。

代码C++

/* * 求最小生成树时,用数组MAX[i][j]表示i到j的最大边权 * 求完后,直接枚举所有不在MST中的边,替换掉最大边权的边,更新答案 * 点的编号从0开始 */const int MAXN = 110;const int INF = 0x3f3f3f3f;bool vis[MAXN];int lowc[MAXN];int pre[MAXN];int MAX[MAXN][MAXN];bool used[MAXN][MAXN];int Prim(int cost[][MAXN], int n){    int ans = 0;    memset(vis, false, sizeof(vis));    memset(MAX, 0, sizeof(MAX));    memset(used, false, sizeof(used));    vis[0] = true;    pre[0] = -1;    lowc[0] = 0;    for (int i = 1; i < n; i++)    {        lowc[i] = cost[0][i];        pre[i] = 0;    }    for (int i = 1; i < n; i++)    {        int minc = INF;        int p = -1;        for (int j = 0; j < n; j++)        {            if (!vis[j] && minc > lowc[j])            {                minc = lowc[j];                p = j;            }        }        if (minc == INF)        {            return -1;        }        ans += minc;        vis[p] = true;        used[p][pre[p]] = used[pre[p]][p] = true;        for (int j = 0; j < n; j++)        {            if (vis[j])            {                MAX[j][p] = MAX[p][j] = max(MAX[j][pre[p]], lowc[p]);            }            if (!vis[j] && lowc[j] > cost[p][j])            {                lowc[j] = cost[p][j];                pre[j] = p;            }        }    }    return ans;}


1 0