次小生成树

ACM模版

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;
}
全部评论

相关推荐

不愿透露姓名的神秘牛友
11-27 10:52
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务